From 0892c1e3d3a76136ea6188ad06cab5498e4a4e56 Mon Sep 17 00:00:00 2001 From: Igor Kozyrenko Date: Tue, 1 Apr 2014 17:14:06 +0300 Subject: [PATCH] APPLINK-6532, Null state for types in rpc_base --- .../rpc_base/include/rpc_base/rpc_base.h | 23 +++- .../rpc_base/include/rpc_base/rpc_base_inl.h | 97 +++++++++++------ .../rpc_base/include/rpc_base/rpc_base_json_inl.h | 117 +++++++++++++++------ test/components/rpc_base/rpc_base_json_test.cc | 49 +++++++++ 4 files changed, 217 insertions(+), 69 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 e7cdd63..8e62936 100644 --- a/src/components/rpc_base/include/rpc_base/rpc_base.h +++ b/src/components/rpc_base/include/rpc_base/rpc_base.h @@ -84,11 +84,20 @@ class PrimitiveType { public: bool is_initialized() const; bool is_valid() const; + bool is_null() const; + void set_to_null(); protected: - PrimitiveType(bool initialized, bool valid); + enum ValueState { + kUninitialized, + kNull, + kInvalid, + kValid + }; + PrimitiveType(ValueState value_state); + static ValueState InitHelper(const Json::Value* value, + bool (Json::Value::*type_check)() const); protected: - bool initialized_; - bool valid_; + ValueState value_state_; }; /* @@ -242,6 +251,10 @@ class Array : public std::vector { bool is_valid() const; bool is_initialized() const; + bool is_null() const; + void set_to_null(); + private: + bool marked_as_null_; }; template @@ -266,6 +279,10 @@ class Map : public std::map { bool is_valid() const; bool is_initialized() const; + bool is_null() const; + void set_to_null(); + private: + bool marked_as_null_; }; template 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 615acf9..6f20afd 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 @@ -64,33 +64,41 @@ bool Range::Includes(U val) const { return min() <= val && val <= max(); } -inline PrimitiveType::PrimitiveType(bool initialized, bool valid) - : initialized_(initialized), valid_(valid) { +inline PrimitiveType::PrimitiveType(ValueState value_state) + : value_state_(value_state) { } inline bool PrimitiveType::is_initialized() const { - return initialized_; + return value_state_ != kUninitialized; } inline bool PrimitiveType::is_valid() const { - return valid_; + 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 */ inline Boolean::Boolean() - : PrimitiveType(false, false), value_(false) { + : PrimitiveType(kUninitialized), + value_(false) { } inline Boolean::Boolean(bool value) - : PrimitiveType(true, true), value_(value) { + : PrimitiveType(kValid), value_(value) { } inline Boolean& Boolean::operator=(bool new_val) { value_ = new_val; - initialized_ = true; - valid_ = true; + value_state_ = kValid; return *this; } @@ -106,21 +114,20 @@ const Range Integer::range_(minval, maxval); template Integer::Integer() - : PrimitiveType(false, false), + : PrimitiveType(kUninitialized), value_(range_.min()) { } template Integer::Integer(IntType value) - : PrimitiveType(true, range_.Includes(value)), + : PrimitiveType(range_.Includes(value) ? kValid : kInvalid), value_(value) { } template Integer& Integer::operator=(IntType new_val) { value_ = new_val; - initialized_ = true; - valid_ = range_.Includes(value_); + value_state_ = range_.Includes(value_) ? kValid : kInvalid; return *this; } @@ -139,13 +146,13 @@ const Range Float::range_( template Float::Float() - : PrimitiveType(false, false), + : PrimitiveType(kUninitialized), value_(range_.min()) { } template Float::Float(double value) - : PrimitiveType(true, range_.Includes(value)), + : PrimitiveType(range_.Includes(value) ? kValid : kInvalid), value_(value) { } @@ -153,8 +160,7 @@ template Float& Float::operator=(double new_val) { value_ = new_val; - initialized_ = true; - valid_ = range_.Includes(new_val); + value_state_ = range_.Includes(new_val) ? kValid : kInvalid; return *this; } @@ -171,27 +177,26 @@ const Range String::length_range_(minlen, maxlen); template String::String() - : PrimitiveType(false, false) { + : PrimitiveType(kUninitialized) { } template String::String(const std::string& value) - : PrimitiveType(true, length_range_.Includes(value.length())), + : PrimitiveType(length_range_.Includes(value.length()) ? kValid : kInvalid), value_(value) { } template String::String(const char* value) - : PrimitiveType(true, true), + : PrimitiveType(kUninitialized), value_(value) { - valid_ = length_range_.Includes(value_.length()); + value_state_ = length_range_.Includes(value_.length()) ? kValid : kInvalid; } template String& String::operator=(const std::string& new_val) { value_ = new_val; - initialized_ = true; - valid_ = length_range_.Includes(new_val.length()); + value_state_ = length_range_.Includes(new_val.length()) ? kValid : kInvalid; return *this; } @@ -205,21 +210,20 @@ String::operator const std::string&() const { */ template Enum::Enum() - : PrimitiveType(false, false), + : PrimitiveType(kUninitialized), value_(EnumType()) { } template Enum::Enum(EnumType value) - : PrimitiveType(true, IsValidEnum(value_)), + : PrimitiveType(IsValidEnum(value_) ? kValid : kInvalid), value_(value) { } template Enum& Enum::operator=(EnumType new_val) { value_ = new_val; - initialized_ = true; - valid_ = IsValidEnum(value_); + value_state_ = IsValidEnum(value_) ? kValid : kInvalid; return *this; } @@ -232,13 +236,15 @@ Enum::operator EnumType() const { * Array class */ template -Array::Array() { +Array::Array() + : marked_as_null_(false) { } template template Array::Array(const U& value) - : ArrayType(value.begin(), value.end() ){ + : ArrayType(value.begin(), value.end()), + marked_as_null_(false) { } template @@ -257,6 +263,9 @@ void Array::push_back(const U& value) { template bool Array::is_valid() const { + if (is_null()) { + return false; + } if (!Range(minsize, maxsize).Includes(this->size())) return false; for (typename ArrayType::const_iterator i = this->begin(); @@ -269,19 +278,31 @@ bool Array::is_valid() const { template bool Array::is_initialized() const { - return !this->empty(); + return is_null() || !this->empty(); +} + +template +bool Array::is_null() const { + return marked_as_null_; +} + +template +void Array::set_to_null() { + marked_as_null_ = true; } /* * Map class */ template -Map::Map() { +Map::Map() + : marked_as_null_(false) { } template template -Map::Map(const U& value) { +Map::Map(const U& value) + : marked_as_null_(false) { for (typename U::const_iterator i = value.begin(), e = value.end(); i != e; ++i) { // Explicitly convert that value to T because all rpc_types have explicit @@ -312,6 +333,8 @@ void Map::insert(const std::pair& value) { template bool Map::is_valid() const { + if (is_null()) + return false; if (!Range(minsize, maxsize).Includes(this->size())) return false; for (typename Map::const_iterator i = this->begin(); @@ -324,7 +347,17 @@ bool Map::is_valid() const { template bool Map::is_initialized() const { - return !this->empty(); + return is_null() || !this->empty(); +} + +template +bool Map::is_null() const { + return marked_as_null_; +} + +template +void Map::set_to_null() { + marked_as_null_ = true; } /* 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 1064daa..aa95682 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 @@ -38,6 +38,22 @@ namespace rpc { +// static +inline PrimitiveType::ValueState PrimitiveType::InitHelper( + const Json::Value* value, + bool (Json::Value::*type_check)() const) { + if (!value) { + return kInvalid; + } + if (value->isNull()) { + return kNull; + } else if ((value->*type_check)()) { + return kValid; + } else { + return kInvalid; + } +} + /* * Composite type */ @@ -80,13 +96,17 @@ void CompositeType::WriteJsonField(const char* field_name, inline Boolean::Boolean(const Json::Value& value) - : PrimitiveType(!value.isNull(), value.isBool()), - value_(valid_ ? value.asBool() : bool()) { + : PrimitiveType(InitHelper(&value, &Json::Value::isBool)), + value_(is_valid() ? value.asBool() : bool()) { } inline Boolean::Boolean(const Json::Value& value, bool def_value) - : PrimitiveType(true, value.isBool() || value.isNull()), - value_(value.isBool() ? value.asBool() : def_value) { + : PrimitiveType(InitHelper(&value, &Json::Value::isBool)), + value_(is_valid() ? value.asBool() : def_value) { + // If there is no value, mark it as valid and use def_value + if (!is_initialized()) { + value_state_ = kValid; + } } inline Json::Value Boolean::ToJsonValue() const { @@ -95,16 +115,34 @@ inline Json::Value Boolean::ToJsonValue() const { template Integer::Integer(const Json::Value& value) - : PrimitiveType(!value.isNull(), - value.isInt() && range_.Includes(value.asInt64())), - value_(valid_ ? value.asInt64() : IntType()) { + : PrimitiveType(InitHelper(&value, &Json::Value::isInt)), + value_() { + if (is_valid()) { + Json::Value::Int64 intval = value.asInt64(); + if (range_.Includes(intval)) { + value_ = intval; + value_state_ = kValid; + } else { + value_state_ = kInvalid; + } + } } template Integer::Integer(const Json::Value& value, IntType def_value) - : PrimitiveType(true, (value.isInt() && range_.Includes(value.asInt64())) - || value.isNull()), - value_(value.isInt() ? value.asInt64() : def_value) { + : PrimitiveType(InitHelper(&value, &Json::Value::isInt)), + value_(def_value) { + if (!is_initialized()) { + value_state_ = kValid; + } else if (is_valid()) { + Json::Value::Int64 intval = value.asInt64(); + if (range_.Includes(intval)) { + value_ = intval; + value_state_ = kValid; + } else { + value_state_ = kInvalid; + } + } } template @@ -114,9 +152,12 @@ Json::Value Integer::ToJsonValue() const { template Float::Float(const Json::Value& value) - : PrimitiveType(!value.isNull(), - value.isDouble() && range_.Includes(value.asDouble())), - value_(valid_ ? value.asDouble() : double()) { + : PrimitiveType(InitHelper(&value, &Json::Value::isDouble)), + value_() { + if (is_valid()) { + value_ = value.asDouble(); + value_state_ = range_.Includes(value_) ? kValid : kInvalid; + } } template @@ -135,10 +176,12 @@ Json::Value Float::ToJsonValue() const { template String::String(const Json::Value& value) - : PrimitiveType(!value.isNull(), - (value.isString() && - length_range_.Includes(value.asString().length()))), - value_(valid_ ? value.asString() : std::string()) { + : PrimitiveType(InitHelper(&value, &Json::Value::isString)), + value_(is_valid() ? value.asString() : std::string()) { + if (is_valid()) { + value_ = value.asString(); + value_state_ = length_range_.Includes(value_.length()) ? kValid : kInvalid; + } } template @@ -156,19 +199,23 @@ Json::Value String::ToJsonValue() const { template Enum::Enum(const Json::Value& value) - : PrimitiveType(!value.isNull(), value.isString()), + : PrimitiveType(InitHelper(&value, &Json::Value::isString)), value_(EnumType()) { - if (valid_) { - valid_ = EnumFromJsonString(value.asString(), &value_); + if (is_valid()) { + value_state_ = + EnumFromJsonString(value.asString(), &value_) ? kValid : kInvalid; } } template Enum::Enum(const Json::Value& value, EnumType def_value) - : PrimitiveType(true, value.isString() || value.isNull()), + : PrimitiveType(InitHelper(&value, &Json::Value::isString)), value_(def_value) { - if (value.isString()) { - valid_ = EnumFromJsonString(value.asString(), &value_); + if (!is_initialized()) { + value_state_ = kValid; + } else if (is_valid()) { + value_state_ = + EnumFromJsonString(value.asString(), &value_) ? kValid : kInvalid; } } @@ -178,18 +225,19 @@ Json::Value Enum::ToJsonValue() const { } template -Array::Array(const Json::Value& value) { - if (value.isArray()) { +Array::Array(const Json::Value& value) + : marked_as_null_(value.isNull()) { + if (marked_as_null_) { + // Do nothing + } else if (value.isArray()) { this->reserve(value.size()); for (Json::Value::const_iterator i = value.begin(); i != value.end(); ++i) { push_back(*i); } - } else if (value.isNull()) { - // Do nothing, keep array empty and uninitialized } else { - // In case of non-array value initialize array with null value + // In case of non-array value initialize array with non-initialized value // so it handled as initialized but invalid - push_back(Json::Value()); + push_back(T()); } } @@ -204,17 +252,18 @@ Json::Value Array::ToJsonValue() const { } template -Map::Map(const Json::Value& value) { - if (value.isObject()) { +Map::Map(const Json::Value& value) + : marked_as_null_(value.isNull()) { + if (marked_as_null_) { + // Do nothing + } else if (value.isObject()) { for (Json::Value::const_iterator i = value.begin(); i != value.end(); ++i) { this->insert(typename MapType::value_type(i.key().asString(), T(*i))); } - } else if (value.isNull()) { - // Do nothing, keep array empty and uninitialized } else { // In case of non-array value initialize array with null value // so it handled as initialized but invalid - this->insert(typename MapType::value_type("", T(Json::Value()))); + this->insert(typename MapType::value_type("", T())); } } diff --git a/test/components/rpc_base/rpc_base_json_test.cc b/test/components/rpc_base/rpc_base_json_test.cc index 63e4034..9a9a708 100644 --- a/test/components/rpc_base/rpc_base_json_test.cc +++ b/test/components/rpc_base/rpc_base_json_test.cc @@ -83,6 +83,13 @@ TEST(ValidatedTypesJson, BooleanFromJsonTest) { ASSERT_EQ(readback.asBool(), true); } +TEST(ValidatedTypesJson, BooleanNullTest) { + Boolean boolean(Value::null); + ASSERT_TRUE(boolean.is_initialized()); + ASSERT_TRUE(boolean.is_null()); + ASSERT_FALSE(boolean.is_valid()); +} + TEST(ValidatedTypesJson, BooleanFromInvalidJsonTest) { Value inv(7); Boolean boolean(inv); @@ -100,6 +107,13 @@ TEST(ValidatedTypesJson, IntegerFromJsonTest) { ASSERT_EQ(readback.asInt(), 42); } +TEST(ValidatedTypesJson, IntegerNullTest) { + Integer integer(Value::null); + ASSERT_TRUE(integer.is_initialized()); + ASSERT_TRUE(integer.is_null()); + ASSERT_FALSE(integer.is_valid()); +} + TEST(ValidatedTypesJson, IntegerFromOverflowingJsonTest) { Value int_val(0xFFFFFFFFFFll); Integer integer(int_val); @@ -131,6 +145,13 @@ TEST(ValidatedTypesJson, FloatFromJsonTest) { ASSERT_EQ(readback.asDouble(), 4.2); } +TEST(ValidatedTypesJson, FloatNullTest) { + Float<1, 7> flt(Value::null); + ASSERT_TRUE(flt.is_initialized()); + ASSERT_TRUE(flt.is_null()); + ASSERT_FALSE(flt.is_valid()); +} + TEST(ValidatedTypesJson, FloatFromInvalidJsonTest) { Value str_val("Hello"); Float<-5, 3> flt(str_val); @@ -148,6 +169,13 @@ TEST(ValidatedTypesJson, StringFromJsonTest) { ASSERT_STREQ(readback.asCString(), "Hello"); } +TEST(ValidatedTypesJson, StringNullTest) { + String<1, 42> str(Value::null); + ASSERT_TRUE(str.is_initialized()); + ASSERT_TRUE(str.is_null()); + ASSERT_FALSE(str.is_valid()); +} + TEST(ValidatedTypesJson, StringFromInvalidJsonTest) { Value int_val(42); String<1, 500> str(int_val); @@ -172,6 +200,13 @@ TEST(ValidatedTypesJson, EnumFromJsonTest) { ASSERT_STREQ(readback.asCString(), "kValue1"); } +TEST(ValidatedTypesJson, EnumNullTest) { + Enum enm(Value::null); + ASSERT_TRUE(enm.is_initialized()); + ASSERT_TRUE(enm.is_null()); + ASSERT_FALSE(enm.is_valid()); +} + TEST(ValidatedTypesJson, EnumFromInvalidJsonTest) { Value str_value("Random string"); Enum enm(str_value); @@ -191,6 +226,20 @@ TEST(ValidatedTypesJson, ArrayFromJsonTest) { ASSERT_EQ(readback.size(), array_value.size()); } +TEST(ValidatedTypesJson, ArrayNullTest) { + Array, 2, 5> arr(Value::null); + ASSERT_TRUE(arr.is_initialized()); + ASSERT_TRUE(arr.is_null()); + ASSERT_FALSE(arr.is_valid()); +} + +TEST(ValidatedTypesJson, MapNullTest) { + Map, 2, 5> map(Value::null); + ASSERT_TRUE(map.is_initialized()); + ASSERT_TRUE(map.is_null()); + ASSERT_FALSE(map.is_valid()); +} + TEST(ValidatedTypesJson, ArrayFromInvalidJsonTest) { Value array_value; array_value.append(Value("Hello")); -- 2.7.4