From 1f6a84183329841e54faae4a0f7deadcda5f8b09 Mon Sep 17 00:00:00 2001 From: Igor Kozyrenko Date: Fri, 4 Apr 2014 19:18:29 +0300 Subject: [PATCH] APPLINK-6532, Nullable wrapper class and it's tests, generator support of nullable attribute Signed-off-by: Justin Dickow Conflicts: test/CMakeLists.txt --- .../rpc_base/include/rpc_base/rpc_base.h | 33 ++++- .../rpc_base/include/rpc_base/rpc_base_inl.h | 55 ++++++-- .../rpc_base/include/rpc_base/rpc_base_json_inl.h | 41 +++++- test/CMakeLists.txt | 150 ++++++++++++++------- test/components/rpc_base/rpc_base_json_test.cc | 37 +++-- test/components/rpc_base/rpc_base_test.cc | 53 ++++++++ .../intergen/src/generated_interface_json_tests.cc | 104 +++++++++++++- test/tools/intergen/test_interface.xml | 21 +++ .../include/cppgen/type_name_code_generator.h | 10 +- tools/intergen/cppgen/src/cppgen/cpp_file.cc | 5 + .../cppgen/src/cppgen/message_factory_function.cc | 2 +- .../cppgen/struct_type_is_initialized_method.cc | 6 +- .../src/cppgen/struct_type_is_valid_method.cc | 10 +- .../cppgen/src/cppgen/type_name_code_generator.cc | 97 +++++++------ .../intergen/model/include/model/composite_type.h | 17 ++- tools/intergen/model/include/model/type.h | 2 + tools/intergen/model/include/model/type_registry.h | 9 +- tools/intergen/model/src/model/composite_type.cc | 26 +++- tools/intergen/model/src/model/type.cc | 4 + tools/intergen/model/src/model/type_registry.cc | 45 +++++-- 20 files changed, 557 insertions(+), 170 deletions(-) diff --git a/src/components/rpc_base/include/rpc_base/rpc_base.h b/src/components/rpc_base/include/rpc_base/rpc_base.h index c28af95..19fa725 100644 --- a/src/components/rpc_base/include/rpc_base/rpc_base.h +++ b/src/components/rpc_base/include/rpc_base/rpc_base.h @@ -59,6 +59,7 @@ template class String; template class Enum; template class Array; template class Map; +template class Nullable; template class Mandatory; template class Optional; @@ -84,12 +85,9 @@ class PrimitiveType { public: bool is_initialized() const; bool is_valid() const; - bool is_null() const; - void set_to_null(); protected: enum ValueState { kUninitialized, - kNull, kInvalid, kValid }; @@ -116,6 +114,10 @@ class CompositeType { const Map& field, Json::Value* json_value); template + static void WriteJsonField(const char* field_name, + const Nullable& field, + Json::Value* json_value); + template static void WriteJsonField(const char* field_name, const Optional& field, Json::Value* json_value); template @@ -291,6 +293,30 @@ class Map : public std::map { }; template +class Nullable : public T { + public: + // Methods + Nullable(); + // Need const and non-const versions to beat all-type accepting constructor + explicit Nullable(Json::Value* value); + explicit Nullable(const Json::Value* value); + template + explicit Nullable(const U& value); + template + Nullable(const Json::Value* value, const U& def_value); + template + Nullable& operator=(const U& new_val); + Json::Value ToJsonValue() const; + + bool is_valid() const; + bool is_initialized() const; + bool is_null() const; + void set_to_null(); + private: + bool marked_null_; +}; + +template class Mandatory : public T { public: // Methods @@ -330,7 +356,6 @@ class Optional { bool is_valid() const; bool is_initialized() const; - bool is_null() const; private: T value_; }; diff --git a/src/components/rpc_base/include/rpc_base/rpc_base_inl.h b/src/components/rpc_base/include/rpc_base/rpc_base_inl.h index 99aba77..e879b38 100644 --- a/src/components/rpc_base/include/rpc_base/rpc_base_inl.h +++ b/src/components/rpc_base/include/rpc_base/rpc_base_inl.h @@ -76,14 +76,6 @@ inline bool PrimitiveType::is_valid() const { return value_state_ == kValid; } -inline bool PrimitiveType::is_null() const { - return value_state_ == kNull; -} - -inline void PrimitiveType::set_to_null() { - value_state_ = kNull; -} - /* * Boolean class */ @@ -361,6 +353,48 @@ void Map::set_to_null() { } /* + * Nullable class + */ +template +Nullable::Nullable() + : marked_null_(false) { +} + +template +template +Nullable::Nullable(const U& value) + : T(value), + marked_null_(false) { +} + +template +template +Nullable& Nullable::operator=(const U& new_val) { + this->T::operator=(new_val); + return *this; +} + +template +bool Nullable::is_valid() const { + return is_null() || T::is_valid(); +} + +template +bool Nullable::is_initialized() const { + return is_null() || T::is_initialized(); +} + +template +bool Nullable::is_null() const { + return marked_null_; +} + +template +void Nullable::set_to_null() { + marked_null_ = true; +} + +/* * Mandatory class */ template @@ -433,11 +467,6 @@ bool Optional::is_initialized() const { return value_.is_initialized(); } -template -bool Optional::is_null() const { - return value_.is_null(); -} - } // namespace rpc diff --git a/src/components/rpc_base/include/rpc_base/rpc_base_json_inl.h b/src/components/rpc_base/include/rpc_base/rpc_base_json_inl.h index 8a4f2bc..8d10f10 100644 --- a/src/components/rpc_base/include/rpc_base/rpc_base_json_inl.h +++ b/src/components/rpc_base/include/rpc_base/rpc_base_json_inl.h @@ -44,9 +44,6 @@ inline PrimitiveType::ValueState PrimitiveType::InitHelper( bool (Json::Value::*type_check)() const) { if (!value) { return kUninitialized; - } - if (value->isNull()) { - return kNull; } else if ((value->*type_check)()) { return kValid; } else { @@ -90,6 +87,14 @@ void CompositeType::WriteJsonField(const char* field_name, // static template void CompositeType::WriteJsonField(const char* field_name, + const Nullable& field, + Json::Value* json_value) { + (*json_value)[field_name] = field.ToJsonValue(); +} + +// static +template +void CompositeType::WriteJsonField(const char* field_name, const Optional& field, Json::Value* json_value) { if (field.is_initialized()) { @@ -274,6 +279,9 @@ Array::Array(const Json::Value* value) template Json::Value Array::ToJsonValue() const { + if (is_null()) { + return Json::Value::null; + } Json::Value array(Json::arrayValue); array.resize(this->size()); for (size_t i = 0; i != this->size(); ++i) { @@ -317,6 +325,9 @@ Map::Map(const Json::Value* value) template Json::Value Map::ToJsonValue() const { + if (is_null()) { + return Json::Value::null; + } Json::Value map(Json::objectValue); for (typename MapType::const_iterator i = this->begin(); i != this->end(); ++i) { map[i->first] = i->second.ToJsonValue(); @@ -325,6 +336,30 @@ Json::Value Map::ToJsonValue() const { } template +Nullable::Nullable(const Json::Value* value) + : T(value), + marked_null_(value != NULL && value->isNull()){ +} + +template +Nullable::Nullable(Json::Value* value) + : T(value), + marked_null_(value != NULL && value->isNull()){ +} + +template +template +Nullable::Nullable(const Json::Value* value, const U& def_value) + : T(value, def_value), + marked_null_(value != NULL && value->isNull()) { +} + +template +inline Json::Value Nullable::ToJsonValue() const { + return marked_null_ ? Json::Value::null : T::ToJsonValue(); +} + +template template Mandatory::Mandatory(const Json::Value* value, const U& def_value) : T(value, def_value) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b25f3ee..6dc4114 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,18 +8,17 @@ find_package(LibXML2 REQUIRED) # --- components tests add_subdirectory(./components) add_subdirectory(./thirdPartyLibs) - -#add_subdirectory(./tools) +add_subdirectory(${CMAKE_SOURCE_DIR}/src/components/policy/test/policy ./policy) +add_subdirectory(./tools) include_directories ( - ../src/thirdPartyLibs/gmock-1.6.0/include + ../src/thirdPartyLibs/gmock-1.7.0/include ../src/thirdPartyLibs/jsoncpp/include/ - ../src/thirdPartyLibs/gmock-1.6.0/gtest/include + ../src/thirdPartyLibs/gmock-1.7.0/gtest/include ../src/thirdPartyLibs/MessageBroker/include ../src/appMain ../src/components/application_manager/include - ../src/components/policies/include ../src/components/hmi_message_handler/include ../src/components/request_watchdog/include ../src/components/media_manager/include @@ -53,48 +52,103 @@ include_directories ( # ${GLIB_glib_2_INCLUDE_DIR} ) -set(LIBRARIES - MediaManager +if (BUILD_TESTS_WITH_HMI) + set(LIBRARIES + test_App_Manager ApplicationManager - gtest - gtest_main - gmock - gmock_main - formatters - HMI_API - v4_protocol_v1_2_no_extra - SmartObjects - RequestWatchdog - policies - ProtocolHandler - Utils - ConfigProfile - #test_JSONHandler_v4_protocol_v2_0_revP - #test_json_handler - #test_SmartObjectTest - #test_FormattersCommandsTest - #test_UtilsTest - #test_RequestWatchdogTest - #test_ProtocolHandlerTest - #test_JSONCPPTest - connectionHandler - MOBILE_API - jsoncpp - TransportManager - HMIMessageHandler - MessageBroker - MessageBrokerClient - MessageBrokerServer - encryption - bluetooth - pthread - avahi-client - avahi-common - ${LibXML2_LIBRARIES} -lxml2 - # rt - # ${GSTREAMER_gstreamer_LIBRARY} - Resumption + MediaManager + gtest + gtest_main + gmock + gmock_main + formatters + HMI_API + v4_protocol_v1_2_no_extra + SmartObjects + RequestWatchdog + policies + ProtocolHandler + Utils + ConfigProfile + #test_JSONHandler_v4_protocol_v2_0_revP + #test_json_handler + #test_SmartObjectTest + #test_FormattersCommandsTest + #test_UtilsTest + #test_RequestWatchdogTest + #test_ProtocolHandlerTest + #test_JSONCPPTest + #test_MobileMessageHandlerTest + connectionHandler + MOBILE_API + jsoncpp +################################################# + TransportManager + HMIMessageHandler + MessageBroker + MessageBrokerClient + MessageBrokerServer + encryption + bluetooth + pthread + avahi-client + avahi-common + ${LibXML2_LIBRARIES} -lxml2 + # rt + # ${GSTREAMER_gstreamer_LIBRARY} + Resumption + ) +else(BUILD_TESTS_WITH_HMI) +set(LIBRARIES + MediaManager + ApplicationManager + gtest + gtest_main + gmock + gmock_main + formatters + HMI_API + v4_protocol_v1_2_no_extra + SmartObjects + RequestWatchdog + #policy + ProtocolHandler + Utils + ConfigProfile + #test_JSONHandler_v4_protocol_v2_0_revP + #test_json_handler + #test_SmartObjectTest + #test_FormattersCommandsTest + #test_UtilsTest + #test_RequestWatchdogTest + #test_ProtocolHandlerTest + #test_JSONCPPTest + #test_MobileMessageHandlerTest + connectionHandler + MOBILE_API + jsoncpp +################################################# + TransportManager + HMIMessageHandler + MessageBroker + MessageBrokerClient + MessageBrokerServer + encryption + bluetooth + pthread + avahi-client + avahi-common + ${LibXML2_LIBRARIES} -lxml2 + # rt + # ${GSTREAMER_gstreamer_LIBRARY} + Resumption ) +endif(BUILD_TESTS_WITH_HMI) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND LIBRARIES dl) +endif() + if (BUILD_USB_SUPPORT) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") @@ -103,11 +157,7 @@ endif() endif() add_executable("test_suit" "./test_suit.cc") +target_link_libraries("test_suit" ${LIBRARIES}) -if (BUILD_TESTS_WITH_HMI) - target_link_libraries("test_suit" test_App_Manager ${LIBRARIES}) -else (BUILD_TESTS_WITH_HMI) - target_link_libraries("test_suit" ${LIBRARIES}) -endif (BUILD_TESTS_WITH_HMI) # vim: set ts=2 sw=2 et: diff --git a/test/components/rpc_base/rpc_base_json_test.cc b/test/components/rpc_base/rpc_base_json_test.cc index 480d800..8ac66d3 100644 --- a/test/components/rpc_base/rpc_base_json_test.cc +++ b/test/components/rpc_base/rpc_base_json_test.cc @@ -86,7 +86,6 @@ TEST(ValidatedTypesJson, BooleanFromJsonTest) { TEST(ValidatedTypesJson, BooleanNullTest) { Boolean boolean(&Value::null); ASSERT_TRUE(boolean.is_initialized()); - ASSERT_TRUE(boolean.is_null()); ASSERT_FALSE(boolean.is_valid()); } @@ -94,7 +93,6 @@ TEST(ValidatedTypesJson, BooleanAbsentValueTest) { Value* novalue = NULL; Boolean boolean(novalue); ASSERT_FALSE(boolean.is_initialized()); - ASSERT_FALSE(boolean.is_null()); ASSERT_FALSE(boolean.is_valid()); } @@ -118,7 +116,6 @@ TEST(ValidatedTypesJson, IntegerFromJsonTest) { TEST(ValidatedTypesJson, IntegerNullTest) { Integer integer(&Value::null); ASSERT_TRUE(integer.is_initialized()); - ASSERT_TRUE(integer.is_null()); ASSERT_FALSE(integer.is_valid()); } @@ -126,7 +123,6 @@ TEST(ValidatedTypesJson, IntegerAbsentValueTest) { Value* novalue = NULL; Integer integer(novalue); ASSERT_FALSE(integer.is_initialized()); - ASSERT_FALSE(integer.is_null()); ASSERT_FALSE(integer.is_valid()); } @@ -164,7 +160,6 @@ TEST(ValidatedTypesJson, FloatFromJsonTest) { TEST(ValidatedTypesJson, FloatNullTest) { Float<1, 7> flt(&Value::null); ASSERT_TRUE(flt.is_initialized()); - ASSERT_TRUE(flt.is_null()); ASSERT_FALSE(flt.is_valid()); } @@ -172,7 +167,6 @@ TEST(ValidatedTypesJson, FloatAbsentValueTest) { Value* novalue = NULL; Float<1, 7> flt(novalue); ASSERT_FALSE(flt.is_initialized()); - ASSERT_FALSE(flt.is_null()); ASSERT_FALSE(flt.is_valid()); } @@ -196,7 +190,6 @@ TEST(ValidatedTypesJson, StringFromJsonTest) { TEST(ValidatedTypesJson, StringNullTest) { String<1, 42> str(&Value::null); ASSERT_TRUE(str.is_initialized()); - ASSERT_TRUE(str.is_null()); ASSERT_FALSE(str.is_valid()); } @@ -211,7 +204,6 @@ TEST(ValidatedTypesJson, StringAbsentValueTest) { Value* novalue = NULL; String<1, 500> str(novalue); ASSERT_FALSE(str.is_initialized()); - ASSERT_FALSE(str.is_null()); ASSERT_FALSE(str.is_valid()); } @@ -235,7 +227,6 @@ TEST(ValidatedTypesJson, EnumFromJsonTest) { TEST(ValidatedTypesJson, EnumNullTest) { Enum enm(&Value::null); ASSERT_TRUE(enm.is_initialized()); - ASSERT_TRUE(enm.is_null()); ASSERT_FALSE(enm.is_valid()); } @@ -243,7 +234,6 @@ TEST(ValidatedTypesJson, EnumAbsentValueTest) { Value* novalue = NULL; Enum enm(novalue); ASSERT_FALSE(enm.is_initialized()); - ASSERT_FALSE(enm.is_null()); ASSERT_FALSE(enm.is_valid()); } @@ -321,7 +311,6 @@ TEST(ValidatedTypesJson, OptionalBoolFromAbsentValueTest) { Value* none = NULL; Optional< Boolean > optional_bool; *optional_bool = Boolean(none); - ASSERT_FALSE(optional_bool.is_null()); ASSERT_FALSE(optional_bool.is_initialized()); // It is ok for Optional value to be absent ASSERT_TRUE(optional_bool.is_valid()); @@ -330,7 +319,6 @@ TEST(ValidatedTypesJson, OptionalBoolFromAbsentValueTest) { TEST(ValidatedTypesJson, OptionalBoolFromNullValueTest) { Optional< Boolean > optional_bool; *optional_bool = Boolean(&Value::null); - ASSERT_TRUE(optional_bool.is_null()); ASSERT_TRUE(optional_bool.is_initialized()); // Optional values should not be absent ASSERT_FALSE(optional_bool.is_valid()); @@ -340,11 +328,34 @@ TEST(ValidatedTypesJson, MandatoryBoolFromAbsentValueTest) { Value* none = NULL; Mandatory< Boolean > mandatory_bool; mandatory_bool = Boolean(none); - ASSERT_FALSE(mandatory_bool.is_null()); ASSERT_FALSE(mandatory_bool.is_initialized()); ASSERT_FALSE(mandatory_bool.is_valid()); } +TEST(ValidatedTypesJson, NullableIntFromNullValueTest) { + Nullable< Integer > nullable_int(&Value::null); + ASSERT_TRUE(nullable_int.is_initialized()); + ASSERT_TRUE(nullable_int.is_valid()); + ASSERT_TRUE(nullable_int.is_null()); +} + +TEST(ValidatedTypesJson, NullableIntFromNonNullValueTest) { + Value json(3); + Nullable< Integer > nullable_int(&json); + ASSERT_TRUE(nullable_int.is_initialized()); + ASSERT_TRUE(nullable_int.is_valid()); + ASSERT_FALSE(nullable_int.is_null()); + ASSERT_EQ(3, nullable_int); +} + +TEST(ValidatedTypesJson, NullableIntFromAbsentValueTest) { + Value* noval = NULL; + Nullable< Integer > nullable_int(noval); + ASSERT_FALSE(nullable_int.is_initialized()); + ASSERT_FALSE(nullable_int.is_valid()); + ASSERT_FALSE(nullable_int.is_null()); +} + TEST(ValidatedTypesJson, OptionalIntFromJsonTest) { Value int_value(42); Optional< Integer > optional_int; diff --git a/test/components/rpc_base/rpc_base_test.cc b/test/components/rpc_base/rpc_base_test.cc index aa8c52d..4a29e47 100644 --- a/test/components/rpc_base/rpc_base_test.cc +++ b/test/components/rpc_base/rpc_base_test.cc @@ -241,6 +241,59 @@ TEST(ValidatedTypes, TestEnumConstructor) { ASSERT_FALSE(te.is_valid()); } +TEST(ValidatedTypes, TestNullableConstructor) { + Nullable< Integer >nullable_int; + ASSERT_FALSE(nullable_int.is_initialized()); + ASSERT_FALSE(nullable_int.is_null()); + ASSERT_FALSE(nullable_int.is_valid()); + nullable_int = 5; + ASSERT_TRUE(nullable_int.is_initialized()); + ASSERT_FALSE(nullable_int.is_null()); + ASSERT_TRUE(nullable_int.is_valid()); + nullable_int.set_to_null(); + ASSERT_TRUE(nullable_int.is_initialized()); + ASSERT_TRUE(nullable_int.is_null()); + ASSERT_TRUE(nullable_int.is_valid()); +} + +TEST(ValidatedTypes, TestOptionalNullableConstructor) { + Optional< Nullable< Integer > > optional_nullable_int; + ASSERT_FALSE(optional_nullable_int.is_initialized()); + ASSERT_FALSE(optional_nullable_int->is_null()); + ASSERT_TRUE(optional_nullable_int.is_valid()); + ASSERT_FALSE(optional_nullable_int); + + *optional_nullable_int = 9; + ASSERT_TRUE(optional_nullable_int.is_initialized()); + ASSERT_FALSE(optional_nullable_int->is_null()); + ASSERT_TRUE(optional_nullable_int.is_valid()); + ASSERT_EQ(9, *optional_nullable_int); + ASSERT_TRUE(optional_nullable_int); + + optional_nullable_int->set_to_null(); + ASSERT_TRUE(optional_nullable_int.is_initialized()); + ASSERT_TRUE(optional_nullable_int->is_null()); + ASSERT_TRUE(optional_nullable_int.is_valid()); +} + +TEST(ValidatedTypes, TestMandatoryNullableConstructor) { + Mandatory< Nullable< Integer > > mandatory_nullable_int; + ASSERT_FALSE(mandatory_nullable_int.is_initialized()); + ASSERT_FALSE(mandatory_nullable_int.is_null()); + ASSERT_FALSE(mandatory_nullable_int.is_valid()); + + mandatory_nullable_int = 9; + ASSERT_TRUE(mandatory_nullable_int.is_initialized()); + ASSERT_FALSE(mandatory_nullable_int.is_null()); + ASSERT_TRUE(mandatory_nullable_int.is_valid()); + ASSERT_EQ(9, mandatory_nullable_int); + + mandatory_nullable_int.set_to_null(); + ASSERT_TRUE(mandatory_nullable_int.is_initialized()); + ASSERT_TRUE(mandatory_nullable_int.is_null()); + ASSERT_TRUE(mandatory_nullable_int.is_valid()); +} + TEST(ValidatedTypes, TestMandatoryConstructor) { Mandatory< Integer > mandatory_int; ASSERT_FALSE(mandatory_int.is_initialized()); diff --git a/test/tools/intergen/src/generated_interface_json_tests.cc b/test/tools/intergen/src/generated_interface_json_tests.cc index 511292e..8812ca2 100644 --- a/test/tools/intergen/src/generated_interface_json_tests.cc +++ b/test/tools/intergen/src/generated_interface_json_tests.cc @@ -119,7 +119,7 @@ TEST_F(GeneratedInterfaceTests, TestHandlerCalled) { TEST_F(GeneratedInterfaceTests, TestFactory) { testing::StrictMock mock; Json::Value json_value; - request::Request* req = request::NewFromJson(json_value, kAddSubMenuID); + request::Request* req = request::NewFromJson(&json_value, kAddSubMenuID); request::AddSubMenu& add_sub_menu_ref = static_cast(*req); EXPECT_CALL(mock, HandleAddSubMenu(testing::Ref(add_sub_menu_ref))) @@ -127,4 +127,106 @@ TEST_F(GeneratedInterfaceTests, TestFactory) { req->HandleWith(&mock); } +TEST_F(GeneratedInterfaceTests, TestNullableStructMember) { + TestStructWithNullableParam with_nullable; + ASSERT_FALSE(with_nullable.is_initialized()); + ASSERT_FALSE(with_nullable.is_valid()); + ASSERT_FALSE(with_nullable.nullableInt.is_valid()); + ASSERT_FALSE(with_nullable.nullableInt.is_null()); + with_nullable.nullableInt.set_to_null(); + ASSERT_TRUE(with_nullable.is_valid()); + ASSERT_TRUE(with_nullable.is_initialized()); + ASSERT_TRUE(with_nullable.nullableInt.is_null()); + ASSERT_TRUE(with_nullable.nullableInt.is_valid()); + ASSERT_TRUE(with_nullable.nullableInt.is_initialized()); +} + +TEST_F(GeneratedInterfaceTests, TestNullableStructMemberNullInitializationFromJson) { + const char* input_json = + "{\"nullableInt\":null}\n"; + Value json_value = JsonValue(input_json); + TestStructWithNullableParam with_nullable(&json_value); + ASSERT_TRUE(with_nullable.is_initialized()); + ASSERT_TRUE(with_nullable.is_valid()); + ASSERT_TRUE(with_nullable.nullableInt.is_null()); + std::string result = writer.write(with_nullable.ToJsonValue()); + ASSERT_EQ(input_json, result); +} + +TEST_F(GeneratedInterfaceTests, TestNullableStructMemberInitializationFromJson) { + const char* input_json = + "{\"nullableInt\":3}\n"; + Value json_value = JsonValue(input_json); + TestStructWithNullableParam with_nullable(&json_value); + ASSERT_TRUE(with_nullable.is_initialized()); + ASSERT_TRUE(with_nullable.is_valid()); + ASSERT_FALSE(with_nullable.nullableInt.is_null()); + ASSERT_EQ(3, with_nullable.nullableInt); + std::string result = writer.write(with_nullable.ToJsonValue()); + ASSERT_EQ(input_json, result); +} + +TEST_F(GeneratedInterfaceTests, TestNullableEnumInitialization) { + TestStructWithNullableStructParam strct_with_nullable; + strct_with_nullable.nullableEnum = IT_DYNAMIC; + strct_with_nullable.nonNullableEnum = IT_STATIC; + ASSERT_TRUE(strct_with_nullable.is_initialized()); + ASSERT_TRUE(strct_with_nullable.is_valid()); + std::string result = writer.write(strct_with_nullable.ToJsonValue()); + const char* awaited_json1 = "{\"nonNullableEnum\":\"STATIC\",\"nullableEnum\":\"DYNAMIC\"}\n"; + ASSERT_EQ(awaited_json1, result); + + strct_with_nullable.nullableEnum.set_to_null(); + ASSERT_TRUE(strct_with_nullable.is_initialized()); + ASSERT_TRUE(strct_with_nullable.is_valid()); + result = writer.write(strct_with_nullable.ToJsonValue()); + const char* awaited_json2 = "{\"nonNullableEnum\":\"STATIC\",\"nullableEnum\":null}\n"; + ASSERT_EQ(awaited_json2, result); +} + +TEST_F(GeneratedInterfaceTests, TestStructWithNullableTypedef) { + StructWithNullableTypedef swntd; + ASSERT_FALSE(swntd.is_initialized()); + ASSERT_FALSE(swntd.is_valid()); + swntd.nullableTdResult = R_SUCCESS; + ASSERT_TRUE(swntd.is_initialized()); + ASSERT_TRUE(swntd.is_valid()); + ASSERT_EQ(R_SUCCESS, swntd.nullableTdResult); + + swntd.nullableTdResult.set_to_null(); + const char* awaited_json = "{\"nullableTdResult\":null}\n"; + std::string result = writer.write(swntd.ToJsonValue()); + ASSERT_EQ(awaited_json, result); +} + +TEST_F(GeneratedInterfaceTests, TestNullingStructWithNullableMapOfNullableInts) { + StructWithNullableMapOfNullableInts nmoni; + ASSERT_FALSE(nmoni.is_initialized()); + ASSERT_FALSE(nmoni.is_valid()); + ASSERT_FALSE(nmoni.nullableMap.is_null()); + nmoni.nullableMap.set_to_null(); + ASSERT_TRUE(nmoni.is_initialized()); + ASSERT_TRUE(nmoni.is_valid()); + ASSERT_TRUE(nmoni.nullableMap.is_null()); + const char* awaited_json = "{\"nullableMap\":null}\n"; + std::string result = writer.write(nmoni.ToJsonValue()); + ASSERT_EQ(awaited_json, result); +} + +TEST_F(GeneratedInterfaceTests, TestNullingValueInStructWithNullableMapOfNullableInts) { + StructWithNullableMapOfNullableInts nmoni; + ASSERT_FALSE(nmoni.is_initialized()); + ASSERT_FALSE(nmoni.is_valid()); + ASSERT_FALSE(nmoni.nullableMap.is_null()); + nmoni.nullableMap["Hello"].set_to_null(); + + ASSERT_TRUE(nmoni.is_initialized()); + ASSERT_TRUE(nmoni.is_valid()); + ASSERT_FALSE(nmoni.nullableMap.is_null()); + ASSERT_TRUE(nmoni.nullableMap["Hello"].is_null()); + const char* awaited_json = "{\"nullableMap\":{\"Hello\":null}}\n"; + std::string result = writer.write(nmoni.ToJsonValue()); + ASSERT_EQ(awaited_json, result); +} + } // namespace test diff --git a/test/tools/intergen/test_interface.xml b/test/tools/intergen/test_interface.xml index 71c453f..273c98c 100644 --- a/test/tools/intergen/test_interface.xml +++ b/test/tools/intergen/test_interface.xml @@ -184,6 +184,15 @@ + + + + + + + + + Enumeration linking function names with function IDs in AppLink protocol. Assumes enumeration starts at value 0. @@ -327,6 +336,18 @@ Test typedef over enum + + + + Test struct having nullable typedef as a param + + + + + + + + Test typedef over array diff --git a/tools/intergen/cppgen/include/cppgen/type_name_code_generator.h b/tools/intergen/cppgen/include/cppgen/type_name_code_generator.h index 5557e12..bc23fb0 100644 --- a/tools/intergen/cppgen/include/cppgen/type_name_code_generator.h +++ b/tools/intergen/cppgen/include/cppgen/type_name_code_generator.h @@ -72,8 +72,10 @@ class TypeNameGenerator: public TypeCodeGenerator { virtual void GenerateCodeForEnum(const Enum* enm); virtual void GenerateCodeForArray(const Array* array); virtual void GenerateCodeForMap(const Map* map); + virtual void GenerateCodeForNullable(const NullableType* nullable); virtual void GenerateCodeForStruct(const Struct* strct); virtual void GenerateCodeForTypedef(const Typedef* tdef); + private: const Interface* interface_; const TypePreferences* preferences_; @@ -117,17 +119,12 @@ class RpcTypeNameGenerator: public TypeCodeGenerator { virtual void GenerateCodeForEnum(const Enum* enm); virtual void GenerateCodeForArray(const Array* array); virtual void GenerateCodeForMap(const Map* map); + virtual void GenerateCodeForNullable(const NullableType* nullable); virtual void GenerateCodeForStruct(const Struct* strct); virtual void GenerateCodeForTypedef(const Typedef* tdef); private: - // Wraps type declaration with "Mandatory" or "Optional" templates - // returns true if type name was wrapped (and thus fully generated). - bool MaybeWrapWithAvailabilitySpecifier(const Type* type); -private: const Interface* interface_; const TypePreferences* preferences_; - bool skip_availaiblity_specifier_; - bool mandatory_; std::stringstream os_; }; @@ -150,6 +147,7 @@ private: virtual void GenerateCodeForEnum(const Enum* enm); virtual void GenerateCodeForArray(const Array* array); virtual void GenerateCodeForMap(const Map* map); + virtual void GenerateCodeForNullable(const NullableType* nullable); virtual void GenerateCodeForStruct(const Struct* strct); virtual void GenerateCodeForTypedef(const Typedef* tdef); private: diff --git a/tools/intergen/cppgen/src/cppgen/cpp_file.cc b/tools/intergen/cppgen/src/cppgen/cpp_file.cc index 5092654..b346e16 100644 --- a/tools/intergen/cppgen/src/cppgen/cpp_file.cc +++ b/tools/intergen/cppgen/src/cppgen/cpp_file.cc @@ -67,6 +67,7 @@ class IncludeTypeHelper: public TypeCodeGenerator { virtual void GenerateCodeForArray(const Array* array); virtual void GenerateCodeForMap(const Map* map); virtual void GenerateCodeForEnum(const Enum* enm); + virtual void GenerateCodeForNullable(const NullableType* nullable); virtual void GenerateCodeForStruct(const Struct* strct); virtual void GenerateCodeForTypedef(const Typedef* tdef); private: @@ -98,6 +99,10 @@ void IncludeTypeHelper::GenerateCodeForEnum(const Enum *enm) { true)); } +void IncludeTypeHelper::GenerateCodeForNullable(const NullableType* nullable) { + nullable->type()->Apply(this); +} + void IncludeTypeHelper::GenerateCodeForStruct(const Struct* strct) { cpp_file_->Include(CppFile::Header( GetTypeInterfaceName(strct) + "/types.h", diff --git a/tools/intergen/cppgen/src/cppgen/message_factory_function.cc b/tools/intergen/cppgen/src/cppgen/message_factory_function.cc index 675a51b..2cfc3ea 100644 --- a/tools/intergen/cppgen/src/cppgen/message_factory_function.cc +++ b/tools/intergen/cppgen/src/cppgen/message_factory_function.cc @@ -55,7 +55,7 @@ MessageFactoryFunction::MessageFactoryFunction( factory_type_(factory_type) { Add(MessageFactoryFunction::Parameter( serialization_type == kJson ? "json" : "reader", - serialization_type == kJson ? "const Json::Value&": "dbus::MessageReader*")); + serialization_type == kJson ? "const Json::Value*": "dbus::MessageReader*")); Add(MessageFactoryFunction::Parameter("function_id", "FunctionID")); } diff --git a/tools/intergen/cppgen/src/cppgen/struct_type_is_initialized_method.cc b/tools/intergen/cppgen/src/cppgen/struct_type_is_initialized_method.cc index 4ca5cd5..effae08 100644 --- a/tools/intergen/cppgen/src/cppgen/struct_type_is_initialized_method.cc +++ b/tools/intergen/cppgen/src/cppgen/struct_type_is_initialized_method.cc @@ -53,16 +53,14 @@ StructTypeIsInitializedMethod::~StructTypeIsInitializedMethod() { void StructTypeIsInitializedMethod::DefineBody(std::ostream* os) const { const Struct::FieldsList& fields = strct_->fields(); - *os << "return" << '\n'; - Indent indent1(*os), indent2(*os); for (size_t i = 0; i != fields.size(); ++i) { const Struct::Field& field = fields[i]; - strmfmt(*os, "{0}.is_initialized() || ", field.name()); + strmfmt(*os, "if ({0}.is_initialized()) return true;\n", field.name()); if ((i % 2) == 1) { *os << endl; } } - *os << "false;" << endl; + *os << "return false;\n"; } } // namespace codegen diff --git a/tools/intergen/cppgen/src/cppgen/struct_type_is_valid_method.cc b/tools/intergen/cppgen/src/cppgen/struct_type_is_valid_method.cc index 67722c2..3a9874e 100644 --- a/tools/intergen/cppgen/src/cppgen/struct_type_is_valid_method.cc +++ b/tools/intergen/cppgen/src/cppgen/struct_type_is_valid_method.cc @@ -53,16 +53,11 @@ StructTypeIsValidMethod::~StructTypeIsValidMethod() { void StructTypeIsValidMethod::DefineBody(std::ostream* os) const { const Struct::FieldsList& fields = strct_->fields(); - *os << "return" << '\n'; - Indent indent1(*os), indent2(*os); for (size_t i = 0; i != fields.size(); ++i) { const Struct::Field& field = fields[i]; - strmfmt(*os, "{0}.is_valid() && ", field.name()); - if ((i % 2) == 1) { - *os << '\n'; - } + strmfmt(*os, "if (!{0}.is_valid()) return false;\n", field.name()); } - *os << func_names::kAdditionalValidation << "();" << endl; + *os << "return "<< func_names::kAdditionalValidation << "();\n"; } StructTypeAdditionalValidationMethod::StructTypeAdditionalValidationMethod( @@ -70,7 +65,6 @@ StructTypeAdditionalValidationMethod::StructTypeAdditionalValidationMethod( : CppFunction(strct->name(), func_names::kAdditionalValidation, "bool", kConst), strct_(strct) { - } StructTypeAdditionalValidationMethod::~StructTypeAdditionalValidationMethod() { diff --git a/tools/intergen/cppgen/src/cppgen/type_name_code_generator.cc b/tools/intergen/cppgen/src/cppgen/type_name_code_generator.cc index a5fc4db..16bd64b 100644 --- a/tools/intergen/cppgen/src/cppgen/type_name_code_generator.cc +++ b/tools/intergen/cppgen/src/cppgen/type_name_code_generator.cc @@ -155,6 +155,11 @@ void TypeNameGenerator::GenerateCodeForMap(const Map* map) { os_ << map_decl_end; } +void TypeNameGenerator::GenerateCodeForNullable(const NullableType* nullable) { + // Just generate a type name without special qualifiers + nullable->type()->Apply(this); +} + void TypeNameGenerator::GenerateCodeForStruct(const Struct* strct) { const char* struct_decl_begin = prefer_reference_type_ ? "const " : ""; const char* struct_decl_end = prefer_reference_type_ ? "&" : ""; @@ -176,16 +181,22 @@ RpcTypeNameGenerator::RpcTypeNameGenerator(const Interface* interface, const Type* type, Availability availability) : interface_(interface), - preferences_(preferences), - skip_availaiblity_specifier_(availability == kUnspecified), - mandatory_(availability == kMandatory) { - if (!skip_availaiblity_specifier_) { - // Arrays, map and typedefs of arrays and maps doesn't need to be marked as - // optional or mandatory because their minimal size indicates whether - // container contents is optional - skip_availaiblity_specifier_ = TypeProperties(type).is_container(); + preferences_(preferences) { + bool wrap_with_availability = availability != kUnspecified; + // Arrays, map and typedefs of arrays and maps doesn't need to be marked as + // optional or mandatory because their minimal size indicates whether + // container contents is optional + if (TypeProperties(type).is_container()) { + wrap_with_availability = false; + } + + if (wrap_with_availability) { + os_ << (availability == kMandatory ? "Mandatory< " : "Optional < "); } type->Apply(this); + if (wrap_with_availability) { + os_ << " >"; + } } RpcTypeNameGenerator::~RpcTypeNameGenerator() { @@ -196,44 +207,33 @@ std::string RpcTypeNameGenerator::result() const { } void RpcTypeNameGenerator::GenerateCodeForBoolean(const Boolean* boolean) { - if (MaybeWrapWithAvailabilitySpecifier(boolean)) { - } else { - os_ << "Boolean"; - } + os_ << "Boolean"; } void RpcTypeNameGenerator::GenerateCodeForInteger(const Integer* integer) { - if (!MaybeWrapWithAvailabilitySpecifier(integer)) { - const char* int_type = StdIntTypeFromRagne(*preferences_, integer->range()); - strmfmt(os_, "Integer<{0}, {1}, {2}>", int_type, integer->range().min(), - integer->range().max()); - } + const char* int_type = StdIntTypeFromRagne(*preferences_, integer->range()); + strmfmt(os_, "Integer<{0}, {1}, {2}>", int_type, integer->range().min(), + integer->range().max()); } void RpcTypeNameGenerator::GenerateCodeForFloat(const Float* flt) { - if (!MaybeWrapWithAvailabilitySpecifier(flt)) { - const Fraction& minval = flt->range().min_fract(); - const Fraction& maxval = flt->range().max_fract(); - strmfmt(os_, "Float<{0}, {1}", minval.numer(), maxval.numer()); - if (minval.denumer() == 1 && maxval.denumer() == 1) { - os_ << ">"; - } else { - strmfmt(os_, ", {0}, {1}>", minval.denumer(), maxval.denumer()); - } + const Fraction& minval = flt->range().min_fract(); + const Fraction& maxval = flt->range().max_fract(); + strmfmt(os_, "Float<{0}, {1}", minval.numer(), maxval.numer()); + if (minval.denumer() == 1 && maxval.denumer() == 1) { + os_ << ">"; + } else { + strmfmt(os_, ", {0}, {1}>", minval.denumer(), maxval.denumer()); } } void RpcTypeNameGenerator::GenerateCodeForString(const String* string) { - if (!MaybeWrapWithAvailabilitySpecifier(string)) { - strmfmt(os_, "String<{0}, {1}>", string->length_range().min(), - string->length_range().max()); - } + strmfmt(os_, "String<{0}, {1}>", string->length_range().min(), + string->length_range().max()); } void RpcTypeNameGenerator::GenerateCodeForEnum(const Enum* enm) { - if (!MaybeWrapWithAvailabilitySpecifier(enm)) { - strmfmt(os_, "Enum<{0}>", TypeNamespacePrefix(interface_, enm) + enm->name()); - } + strmfmt(os_, "Enum<{0}>", TypeNamespacePrefix(interface_, enm) + enm->name()); } void RpcTypeNameGenerator::GenerateCodeForArray(const Array* array) { @@ -248,28 +248,19 @@ void RpcTypeNameGenerator::GenerateCodeForMap(const Map* map) { strmfmt(os_, ", {0}, {1} >", map->range().min(), map->range().max()); } -void RpcTypeNameGenerator::GenerateCodeForStruct(const Struct* strct) { - if (!MaybeWrapWithAvailabilitySpecifier(strct)) { - os_ << TypeNamespacePrefix(interface_, strct) + strct->name(); - } +void RpcTypeNameGenerator::GenerateCodeForNullable( + const NullableType* nullable) { + os_ << "Nullable < "; + nullable->type()->Apply(this); + os_ << " >"; } -void RpcTypeNameGenerator::GenerateCodeForTypedef(const Typedef* tdef) { - if (!MaybeWrapWithAvailabilitySpecifier(tdef)) { - os_ << TypeNamespacePrefix(interface_, tdef) + tdef->name(); - } +void RpcTypeNameGenerator::GenerateCodeForStruct(const Struct* strct) { + os_ << TypeNamespacePrefix(interface_, strct) + strct->name(); } -bool RpcTypeNameGenerator::MaybeWrapWithAvailabilitySpecifier(const Type* type) { - if (skip_availaiblity_specifier_) { - return false; - } else { - skip_availaiblity_specifier_ = true; - os_ << (mandatory_ ? "Mandatory< " : "Optional < "); - type->Apply(this); - os_ << " >"; - return true; - } +void RpcTypeNameGenerator::GenerateCodeForTypedef(const Typedef* tdef) { + os_ << TypeNamespacePrefix(interface_, tdef) + tdef->name(); } TypeProperties::TypeProperties(const Type* type) @@ -304,6 +295,10 @@ void TypeProperties::GenerateCodeForMap(const Map* map) { container_ = true; } +void TypeProperties::GenerateCodeForNullable(const NullableType* nullable) { + nullable->type()->Apply(this); +} + void TypeProperties::GenerateCodeForStruct(const Struct* strct) { } diff --git a/tools/intergen/model/include/model/composite_type.h b/tools/intergen/model/include/model/composite_type.h index e26a394..5ec76e4 100644 --- a/tools/intergen/model/include/model/composite_type.h +++ b/tools/intergen/model/include/model/composite_type.h @@ -207,9 +207,24 @@ class Struct::Field { }; /* - * Typedef type + * Nullable type decorator */ +class NullableType : public Type { + public: + // Methods + NullableType(const Type* type); + const Type* type() const; + bool operator<(const NullableType& that) const; + // codegen::Type methods + virtual TypeCodeGenerator* Apply(TypeCodeGenerator* code_generator) const; + virtual const ConstantsCreator* SupportsConstants() const; + private: + const Type* type_; +}; +/* + * Typedef type + */ class Typedef : public Type { public: Typedef(const Interface* interface, diff --git a/tools/intergen/model/include/model/type.h b/tools/intergen/model/include/model/type.h index b663907..25aa55b 100644 --- a/tools/intergen/model/include/model/type.h +++ b/tools/intergen/model/include/model/type.h @@ -44,6 +44,7 @@ class Enum; class Array; class Map; class Struct; +class NullableType; class Typedef; class ConstantsCreator; @@ -65,6 +66,7 @@ class TypeCodeGenerator { virtual void GenerateCodeForEnum(const Enum* enm); virtual void GenerateCodeForArray(const Array* array); virtual void GenerateCodeForMap(const Map* map); + virtual void GenerateCodeForNullable(const NullableType* nullable); virtual void GenerateCodeForStruct(const Struct* strct); virtual void GenerateCodeForTypedef(const Typedef* tdef); virtual ~TypeCodeGenerator(); diff --git a/tools/intergen/model/include/model/type_registry.h b/tools/intergen/model/include/model/type_registry.h index b15ca8e..6263d28 100644 --- a/tools/intergen/model/include/model/type_registry.h +++ b/tools/intergen/model/include/model/type_registry.h @@ -108,8 +108,11 @@ class TypeRegistry { // if |get_array| is true array is created, otherwise map is created // Returns false and prints to cerr on error bool GetContainer(const pugi::xml_node& params, const Type** type, - bool get_array); - bool GetNonArray(const pugi::xml_node& params, const Type** type); + bool get_array, bool container_nullable); + bool GetNonArray(const pugi::xml_node& params, + const Type** type, + bool nullable); + bool GetNullable(const Type* original_type, const Type** type); bool GetEnum(const std::string& name, const Type** type) const; bool GetStruct(const std::string& name, const Type** type) const; bool GetTypedef(const std::string& name, const Type** type) const; @@ -118,6 +121,7 @@ class TypeRegistry { bool IsRegisteredEnum(const std::string& enum_name) const; bool IsRegisteredStruct(const std::string& struct_name) const; bool IsRegisteredTypedef(const std::string& typedef_name) const; + // Returns true if given type is defined in external interface bool IsExternalType(const std::string& full_type_name) const; private: // fields @@ -129,6 +133,7 @@ class TypeRegistry { const ModelFilter* model_filter_; std::set arrays_; std::set maps_; + std::set nullables_; EnumList enums_; utils::StdContainerDeleter enums_deleter_; EnumByName enum_by_name_; diff --git a/tools/intergen/model/src/model/composite_type.cc b/tools/intergen/model/src/model/composite_type.cc index ced54ef..f499560 100644 --- a/tools/intergen/model/src/model/composite_type.cc +++ b/tools/intergen/model/src/model/composite_type.cc @@ -313,6 +313,31 @@ Struct::Field::Field(const Type* type, const std::string& name, bool mandatory, platform_(platform){ } +NullableType::NullableType(const Type* type) + : type_(type) { +} + +const Type* NullableType::type() const { + return type_; +} + +bool NullableType::operator<(const NullableType& that) const { + // Simple pointer comparison should be enough here + if (type_ != that.type_) { + return type_ < that.type_; + } +} + +TypeCodeGenerator* NullableType::Apply( + TypeCodeGenerator* code_generator) const { + code_generator->GenerateCodeForNullable(this); + return code_generator; +} + +const ConstantsCreator* NullableType::SupportsConstants() const { + return type_->SupportsConstants(); +} + Typedef::Typedef(const Interface* interface, const std::string& name, const Type* type, @@ -328,7 +353,6 @@ const Description& Typedef::description() const { return description_; } - const Interface& Typedef::interface() const { return *interface_; } diff --git a/tools/intergen/model/src/model/type.cc b/tools/intergen/model/src/model/type.cc index 909736f..8491680 100644 --- a/tools/intergen/model/src/model/type.cc +++ b/tools/intergen/model/src/model/type.cc @@ -64,6 +64,10 @@ void TypeCodeGenerator::GenerateCodeForArray(const Array* array) { void TypeCodeGenerator::GenerateCodeForMap(const Map* map) { } +void TypeCodeGenerator::GenerateCodeForNullable( + const NullableType* nullable) { +} + void TypeCodeGenerator::GenerateCodeForStruct(const Struct* strct) { } diff --git a/tools/intergen/model/src/model/type_registry.cc b/tools/intergen/model/src/model/type_registry.cc index e02f9ef..94a75b4 100644 --- a/tools/intergen/model/src/model/type_registry.cc +++ b/tools/intergen/model/src/model/type_registry.cc @@ -80,17 +80,18 @@ bool TypeRegistry::init(const pugi::xml_node& xml) { bool TypeRegistry::GetCompositeType(const pugi::xml_node& params, const Type** type) { bool is_array = params.attribute("array").as_bool(false); bool is_map = params.attribute("map").as_bool(false); + bool is_nullable = params.attribute("nullable").as_bool(false); if (is_array && is_map) { strmfmt(std::cerr, "Entity {0} has both map and array attributes specified", params.attribute("name").as_string("")); return false; } if (is_map) { - return GetContainer(params, type, false); + return GetContainer(params, type, false, is_nullable); } else if (is_array) { - return GetContainer(params, type, true); + return GetContainer(params, type, true, is_nullable); } else { - return GetNonArray(params, type); + return GetNonArray(params, type, is_nullable); } } @@ -225,9 +226,11 @@ bool TypeRegistry::AddTypedef(const pugi::xml_node& xml_typedef) { } bool TypeRegistry::GetContainer(const pugi::xml_node& params, const Type** type, - bool get_array) { + bool get_array, bool container_nullable) { const Type* element_type = NULL; - if (GetNonArray(params, &element_type)) { + bool null_values_allowed = + params.attribute("null_values_allowed").as_bool(false); + if (GetNonArray(params, &element_type, null_values_allowed)) { assert(element_type); std::string minsize_str = params.attribute("minsize").as_string("0"); std::string maxsize_str = params.attribute("maxsize").as_string("0"); @@ -241,7 +244,11 @@ bool TypeRegistry::GetContainer(const pugi::xml_node& params, const Type** type, *type = &*maps_.insert(Map(element_type, Map::Range(minsize, maxsize))) .first; } - return true; + if (container_nullable) { + return GetNullable(*type, type); + } else { + return true; + } } else { std::cerr << "Incorrect array size range: " << minsize_str << ", " << maxsize_str << std::endl; @@ -251,26 +258,40 @@ bool TypeRegistry::GetContainer(const pugi::xml_node& params, const Type** type, } bool TypeRegistry::GetNonArray(const pugi::xml_node& params, - const Type** type) { + const Type** type, bool nullable) { + bool type_found = false; pugi::xml_attribute type_name = params.attribute("type"); if (type_name) { std::string type_name_str = type_name.value(); BuiltinTypeRegistry::BuiltInType builtin_type = builtin_type_registry_ ->BuiltInTypeByName(type_name_str); if (BuiltinTypeRegistry::kNotABuiltInType != builtin_type) { - return builtin_type_registry_->GetType(builtin_type, params, type); + type_found = builtin_type_registry_->GetType(builtin_type, params, type); } else if (IsExternalType(type_name_str)) { - return GetExternalType(type_name_str, type); + type_found = GetExternalType(type_name_str, type); } else if (*type = GetType(type_name_str)) { - return true; + type_found = true; } else { std::cerr << "Unregistered type: " << type_name_str << std::endl; - return false; + type_found = false; } } else { std::cerr << "Absent type name" << std::endl; - return false; + type_found = false; } + + if (type_found && nullable) { + type_found = GetNullable(*type, type); + } + return type_found; +} + +bool TypeRegistry::GetNullable(const Type* original_type, + const Type** type) { + std::set::const_iterator inserted = + nullables_.insert(NullableType(original_type)).first; + *type = &*inserted; + return true; } bool TypeRegistry::GetEnum(const std::string& name, const Type** type) const { -- 2.7.4