APPLINK-6532, Nullable wrapper class and it's tests, generator support of nullable...
authorIgor Kozyrenko <IKozyrenko@luxoft.com>
Fri, 4 Apr 2014 16:18:29 +0000 (19:18 +0300)
committerJustin Dickow <jjdickow@gmail.com>
Tue, 8 Jul 2014 22:54:16 +0000 (18:54 -0400)
Signed-off-by: Justin Dickow <jjdickow@gmail.com>
Conflicts:
test/CMakeLists.txt

20 files changed:
src/components/rpc_base/include/rpc_base/rpc_base.h
src/components/rpc_base/include/rpc_base/rpc_base_inl.h
src/components/rpc_base/include/rpc_base/rpc_base_json_inl.h
test/CMakeLists.txt
test/components/rpc_base/rpc_base_json_test.cc
test/components/rpc_base/rpc_base_test.cc
test/tools/intergen/src/generated_interface_json_tests.cc
test/tools/intergen/test_interface.xml
tools/intergen/cppgen/include/cppgen/type_name_code_generator.h
tools/intergen/cppgen/src/cppgen/cpp_file.cc
tools/intergen/cppgen/src/cppgen/message_factory_function.cc
tools/intergen/cppgen/src/cppgen/struct_type_is_initialized_method.cc
tools/intergen/cppgen/src/cppgen/struct_type_is_valid_method.cc
tools/intergen/cppgen/src/cppgen/type_name_code_generator.cc
tools/intergen/model/include/model/composite_type.h
tools/intergen/model/include/model/type.h
tools/intergen/model/include/model/type_registry.h
tools/intergen/model/src/model/composite_type.cc
tools/intergen/model/src/model/type.cc
tools/intergen/model/src/model/type_registry.cc

index c28af95..19fa725 100644 (file)
@@ -59,6 +59,7 @@ template<size_t minlen, size_t maxlen> class String;
 template<typename T> class Enum;
 template<typename T, size_t minsize, size_t maxsize> class Array;
 template<typename T, size_t minsize, size_t maxsize> class Map;
+template<typename T> class Nullable;
 template<typename T> class Mandatory;
 template<typename T> 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<T, minsize, maxsize>& field,
                              Json::Value* json_value);
   template<class T>
+  static void WriteJsonField(const char* field_name,
+                             const Nullable<T>& field,
+                             Json::Value* json_value);
+  template<class T>
   static void WriteJsonField(const char* field_name, const Optional<T>& field,
                              Json::Value* json_value);
   template<class T>
@@ -291,6 +293,30 @@ class Map : public std::map<std::string, T> {
 };
 
 template<typename T>
+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<typename U>
+  explicit Nullable(const U& value);
+  template<typename U>
+  Nullable(const Json::Value* value, const U& def_value);
+  template<typename U>
+  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<typename T>
 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_;
 };
index 99aba77..e879b38 100644 (file)
@@ -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<T, minsize, maxsize>::set_to_null() {
 }
 
 /*
+ * Nullable class
+ */
+template<typename T>
+Nullable<T>::Nullable()
+    : marked_null_(false) {
+}
+
+template<typename T>
+template<typename U>
+Nullable<T>::Nullable(const U& value)
+    : T(value),
+      marked_null_(false) {
+}
+
+template<typename T>
+template<typename U>
+Nullable<T>& Nullable<T>::operator=(const U& new_val) {
+  this->T::operator=(new_val);
+  return *this;
+}
+
+template<typename T>
+bool Nullable<T>::is_valid() const {
+  return is_null() || T::is_valid();
+}
+
+template<typename T>
+bool Nullable<T>::is_initialized() const {
+  return is_null() || T::is_initialized();
+}
+
+template<typename T>
+bool Nullable<T>::is_null() const {
+  return marked_null_;
+}
+
+template<typename T>
+void Nullable<T>::set_to_null() {
+  marked_null_ = true;
+}
+
+/*
  * Mandatory class
  */
 template<typename T>
@@ -433,11 +467,6 @@ bool Optional<T>::is_initialized() const {
   return value_.is_initialized();
 }
 
-template<typename T>
-bool Optional<T>::is_null() const {
-  return value_.is_null();
-}
-
 }  // namespace rpc
 
 
index 8a4f2bc..8d10f10 100644 (file)
@@ -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<class T>
 void CompositeType::WriteJsonField(const char* field_name,
+                    const Nullable<T>& field,
+                    Json::Value* json_value) {
+  (*json_value)[field_name] = field.ToJsonValue();
+}
+
+// static
+template<class T>
+void CompositeType::WriteJsonField(const char* field_name,
                                    const Optional<T>& field,
                                    Json::Value* json_value) {
   if (field.is_initialized()) {
@@ -274,6 +279,9 @@ Array<T, minsize, maxsize>::Array(const Json::Value* value)
 
 template<typename T, size_t minsize, size_t maxsize>
 Json::Value Array<T, minsize, maxsize>::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<T, minsize, maxsize>::Map(const Json::Value* value)
 
 template<typename T, size_t minsize, size_t maxsize>
 Json::Value Map<T, minsize, maxsize>::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<T, minsize, maxsize>::ToJsonValue() const {
 }
 
 template<typename T>
+Nullable<T>::Nullable(const Json::Value* value)
+    : T(value),
+      marked_null_(value != NULL && value->isNull()){
+}
+
+template<typename T>
+Nullable<T>::Nullable(Json::Value* value)
+    : T(value),
+      marked_null_(value != NULL && value->isNull()){
+}
+
+template<typename T>
+template<typename U>
+Nullable<T>::Nullable(const Json::Value* value, const U& def_value)
+    : T(value, def_value),
+      marked_null_(value != NULL && value->isNull()) {
+}
+
+template<typename T>
+inline Json::Value Nullable<T>::ToJsonValue() const {
+  return marked_null_ ? Json::Value::null : T::ToJsonValue();
+}
+
+template<typename T>
 template<typename U>
 Mandatory<T>::Mandatory(const Json::Value* value, const U& def_value)
     : T(value, def_value) {
index b25f3ee..6dc4114 100644 (file)
@@ -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:
index 480d800..8ac66d3 100644 (file)
@@ -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<int32_t, -5, 192> 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<int32_t, -5, 192> 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<TestEnum> 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<TestEnum> 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<int8_t, 1, 15> > 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<int8_t, 1, 15> > 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<int8_t, 1, 15> > 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<int64_t, 42, 43> > optional_int;
index aa8c52d..4a29e47 100644 (file)
@@ -241,6 +241,59 @@ TEST(ValidatedTypes, TestEnumConstructor) {
   ASSERT_FALSE(te.is_valid());
 }
 
+TEST(ValidatedTypes, TestNullableConstructor) {
+  Nullable< Integer<int8_t, 2, 10> >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<int8_t, 2, 10> > > 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<int8_t, 2, 10> > > 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<int8_t, 2, 10> > mandatory_int;
   ASSERT_FALSE(mandatory_int.is_initialized());
index 511292e..8812ca2 100644 (file)
@@ -119,7 +119,7 @@ TEST_F(GeneratedInterfaceTests, TestHandlerCalled) {
 TEST_F(GeneratedInterfaceTests, TestFactory) {
   testing::StrictMock<TestRequestHandlerMock> 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<request::AddSubMenu&>(*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
index 71c453f..273c98c 100644 (file)
     </param>
   </struct>
 
+  <struct name="TestStructWithNullableParam">
+    <param name="nullableInt" nullable="true" type="Integer" minavlue="0" maxvalue="42" />
+  </struct>
+
+  <struct name="TestStructWithNullableStructParam">
+    <param name="nullableEnum" nullable="true" type="ImageType"/>
+    <param name="nonNullableEnum" nullable="true" type="ImageType"/>
+  </struct>
+
   <enum name="FunctionID" internal_scope="base">
     <description>Enumeration linking function names with function IDs in AppLink protocol.</description>
     <description>Assumes enumeration starts at value 0.</description>
       Test typedef over enum
     </description>
   </typedef>
+
+  <struct name="StructWithNullableTypedef">
+    <description>
+      Test struct having nullable typedef as a param
+    </description>
+    <param name="nullableTdResult" type="TdResult" nullable="true"/>
+  </struct>
+
+  <struct name="StructWithNullableMapOfNullableInts">
+    <param name="nullableMap" type="Integer" minvalue="0" maxvalue="42" map="true" minsize="1" maxsize="5" nullable="true" null_values_allowed="true" />
+  </struct>
+
   <typedef name="TdResultArray" type="TdResult" array="true" minsize="1" maxsize="10">
     <description>
       Test typedef over array
index 5557e12..bc23fb0 100644 (file)
@@ -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:
index 5092654..b346e16 100644 (file)
@@ -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",
index 675a51b..2cfc3ea 100644 (file)
@@ -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"));
 }
 
index 4ca5cd5..effae08 100644 (file)
@@ -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
index 67722c2..3a9874e 100644 (file)
@@ -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() {
index a5fc4db..16bd64b 100644 (file)
@@ -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) {
 }
 
index e26a394..5ec76e4 100644 (file)
@@ -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,
index b663907..25aa55b 100644 (file)
@@ -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();
index b15ca8e..6263d28 100644 (file)
@@ -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<Array> arrays_;
   std::set<Map> maps_;
+  std::set<NullableType> nullables_;
   EnumList enums_;
   utils::StdContainerDeleter<EnumList> enums_deleter_;
   EnumByName enum_by_name_;
index ced54ef..f499560 100644 (file)
@@ -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_;
 }
index 909736f..8491680 100644 (file)
@@ -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) {
 }
 
index e02f9ef..94a75b4 100644 (file)
@@ -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<NullableType>::const_iterator inserted =
+      nullables_.insert(NullableType(original_type)).first;
+  *type = &*inserted;
+  return true;
 }
 
 bool TypeRegistry::GetEnum(const std::string& name, const Type** type) const {