From: Marcin Siodelski Date: Wed, 30 Jan 2019 17:28:58 +0000 (+0100) Subject: [#429] Updated StampedValue to support strings, integers, bool and real. X-Git-Tag: 429-Updated-StampedValue-to-support-reals_base~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a88c177e27d8f43cbb66b210953f93743e7707b2;p=thirdparty%2Fkea.git [#429] Updated StampedValue to support strings, integers, bool and real. --- diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc index eb56ff7c1c..f62a16f153 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc @@ -492,7 +492,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllGlobalParameters4) { cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(), StampedValue::create("name1", "value1")); cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(), - StampedValue::create("name2", 65)); + StampedValue::create("name2", + Element::create(static_cast(65)))); cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(), StampedValue::create("name3", "value3")); @@ -531,7 +532,7 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedGlobalParameters4) { cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(), value); - value = StampedValue::create("name2", 65); + value = StampedValue::create("name2", Element::create(static_cast(65))); value->setModificationTime(timestamps_["today"]); cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(), value); diff --git a/src/lib/cc/stamped_value.cc b/src/lib/cc/stamped_value.cc index 6efb7f81e5..25577388f8 100644 --- a/src/lib/cc/stamped_value.cc +++ b/src/lib/cc/stamped_value.cc @@ -11,48 +11,122 @@ namespace isc { namespace data { -StampedValue::StampedValue(const std::string& name, - const std::string& value) +StampedValue::StampedValue(const std::string& name) + : StampedElement(), name_(name), value_() { +} + +StampedValue::StampedValue(const std::string& name, const ElementPtr& value) : StampedElement(), name_(name), value_(value) { + validateConstruct(); } -StampedValue::StampedValue(const std::string& name, - const int64_t value) - : StampedElement(), name_(name), value_() { +StampedValue::StampedValue(const std::string& name, const std::string& value) + : StampedElement(), name_(name), value_(Element::create(value)) { + validateConstruct(); +} - try { - value_ = boost::lexical_cast(value); - } catch (...) { - isc_throw(BadValue, "unable to cast value " << value - << " to a string"); - } +StampedValuePtr +StampedValue::create(const std::string& name) { + return (StampedValuePtr(new StampedValue(name))); } StampedValuePtr -StampedValue::create(const std::string& name, - const std::string& value) { +StampedValue::create(const std::string& name, const ElementPtr& value) { return (StampedValuePtr(new StampedValue(name, value))); } StampedValuePtr -StampedValue::create(const std::string& name, - const int64_t value) { +StampedValue::create(const std::string& name, const std::string& value) { return (StampedValuePtr(new StampedValue(name, value))); } +int +StampedValue::getType() const { + if (!value_) { + isc_throw(InvalidOperation, "StampedValue: attempt to retrieve the " + "type of the null value for the '" << name_ + << "' parameter"); + } + + return (value_->getType()); +} + +std::string +StampedValue::getValue() const { + validateAccess(Element::string); + + try { + switch (static_cast(value_->getType())) { + case Element::string: + return (value_->stringValue()); + case Element::integer: + return (boost::lexical_cast(value_->intValue())); + case Element::boolean: + return (value_->boolValue() ? "1" : "0"); + case Element::real: + return (boost::lexical_cast(value_->doubleValue())); + default: + // Impossible condition. + isc_throw(TypeError, "StampedValue: invalid type of the '" + << name_ << "' parameter"); + } + + } catch (const boost::bad_lexical_cast& ex) { + isc_throw(BadValue, "StampedValue: unable to convert the value of " + "the parameter '" << name_ << "' to string"); + } + return (value_->stringValue()); +} int64_t StampedValue::getSignedIntegerValue() const { - if (!value_.empty()) { - try { - return (boost::lexical_cast(value_)); - } catch (...) { - isc_throw(BadValue, "unable to cast value " << value_ - << " to a signed integer"); - } + validateAccess(Element::integer); + return (value_->intValue()); +} + +bool +StampedValue::getBoolValue() const { + validateAccess(Element::boolean); + return (value_->boolValue()); +} + +double +StampedValue::getDoubleValue() const { + validateAccess(Element::real); + return (value_->doubleValue()); +} + +void +StampedValue::validateConstruct() const { + if (!value_) { + isc_throw(BadValue, "StampedValue: provided value of the '" + << name_ << "' parameter is NULL"); + } + + if ((value_->getType() != Element::string) && + (value_->getType() != Element::integer) && + (value_->getType() != Element::boolean) && + (value_->getType() != Element::real)) { + isc_throw(TypeError, "StampedValue: provided value of the '" + << name_ << "' parameter has invalid type: " + << Element::typeToName(static_cast(value_->getType()))); + } +} + +void +StampedValue::validateAccess(Element::types type) const { + if (!value_) { + isc_throw(InvalidOperation, "StampedValue: attempt to get null value " + "of the '" << name_ << "' parameter"); } - return (0); + if ((type != Element::string) && (type != value_->getType())) { + isc_throw(TypeError, "StampedValue: attempt to access a '" + << name_ << "' parameter as " << Element::typeToName(type) + << ", but this parameter has " + << Element::typeToName(static_cast(value_->getType())) + << " type"); + } } ElementPtr diff --git a/src/lib/cc/stamped_value.h b/src/lib/cc/stamped_value.h index eda2f94155..0ffcdb7c9e 100644 --- a/src/lib/cc/stamped_value.h +++ b/src/lib/cc/stamped_value.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -25,21 +25,38 @@ class StampedValue; /// @brief Pointer to the stamped value. typedef boost::shared_ptr StampedValuePtr; -/// @brief This class represents string or signed integer configuration -/// element associated with the modification timestamp. +/// @brief This class represents a named configuration parameter, +/// e.g. global parameter of the DHCP server. /// /// Global configuration elements having simple types, e.g. DHCP /// timers, need to be associatied with modification timestamps. /// This association is made by deriving from @c StampedElement. -/// Values can be both integers and strings. Because strings are -/// more flexible, configuration elements are always held as strings -/// in the configuration backends. This class reflects a single value -/// held in the database. The value can be converted to an integer or -/// can be returned as a string. +/// The values can be strings, integers, booleans or real numbers. +/// +/// Because the strings are more flexible, configuration elements +/// are always held as strings in the configuration backends. This +/// class reflects a single value held in the database. The value +/// can be return in its orginal type or can be returned as a +/// string. Also the null values are allowed. class StampedValue : public StampedElement { public: - /// @brief Constructor. + /// @brief Constructor creating a null value. + /// + /// @param name Name of the value. + StampedValue(const std::string& name); + + /// @brief Constructor creating a value from the @c Element. + /// + /// @param name Name of the value. + /// @param value Value encapsulated in the @c Element object. + /// + /// @throw BadValue if the value is null. + /// @throw TypeError if the value is neither a string, integer, + /// bool nor real. + StampedValue(const std::string& name, const ElementPtr& value); + + /// @brief Constructor creating a string value. /// /// Creates stamped value from a string. /// @@ -47,41 +64,68 @@ public: /// @param value Value to be set. StampedValue(const std::string& name, const std::string& value); - /// @brief Constructor. - /// - /// Creates stamped value from the signed integer. + /// @brief Factory function creating a null value. /// /// @param name Name of the value. - /// @param value Value to be set. - explicit StampedValue(const std::string& name, const int64_t value); + static StampedValuePtr create(const std::string& name); - /// @brief Convenience function creating shared pointer to the object. + /// @brief Factory function creating a value from the @c Element. /// /// @param name Name of the value. - /// @param value String value to be encapsulated by this object. + /// @param value Value encapsulated in the @c Element object. + /// + /// @throw BadValue if the value is null. + /// @throw TypeError if the value is neither a string, integer, + /// bool nor real. static StampedValuePtr create(const std::string& name, - const std::string& value); + const ElementPtr& value); - /// @brief Convenience function creating shared Pointer to the object. + /// @brief Factory function creating a string value. + /// + /// Creates stamped value from a string. /// /// @param name Name of the value. - /// @param value Integer value to be encapsulated by this object. + /// @param value Value to be set. static StampedValuePtr create(const std::string& name, - const int64_t value); + const std::string& value); + + /// @brief Returns a type of the value. + /// + /// @return Type of the value as integer. It can be compared + /// with the @c Element::getType() output. + /// @throw InvalidOperation if the value is null. + int getType() const; /// @brief Returns value name. + /// + /// @return Value name. std::string getName() const { return (name_); } /// @brief Returns value as string. - std::string getValue() const { - return (value_); + /// + /// It is allowed to call this function for all supported data + /// types. They are converted to a string. For example, a real + /// number of 1.4 will be returned as "1.4". The boolean true + /// value will be returned as "1" etc. + /// + /// @return Stored value as string. + /// @throw InvalidOperation if the value is null. + std::string getValue() const; + + /// @brief Checks if the value is null. + /// + /// @return true if the value is null, false otherwise. + bool amNull() const { + return (!value_); } /// @brief Returns value as signed integer. /// - /// @throw BadValue if the value can't be converted to an integer. + /// @return Stored value as a signed integer. + /// @throw TypeError if the value is not of @c Element::integer + /// type. int64_t getSignedIntegerValue() const; /// @brief Creates an Element with the appropriate value @@ -96,13 +140,49 @@ public: /// type is unsupported. ElementPtr toElement(const Element::types etype); + /// @brief Returns value as a boolean. + /// + /// @return Stored value as a boolean. + /// @throw TypeError if the value is not of @c Element::boolean + /// type. + bool getBoolValue() const; + + /// @brief Returns value as a real number. + /// + /// @return Stored value as a real number. + /// @throw TypeError if the value is not of @c Element::real + /// type. + double getDoubleValue() const; + private: + /// @brief Checks if the values passed to the constructors + /// were correct. + /// + /// This is called from the constructors. + /// + /// @throw BadValue if the value is null. + /// @throw TypeError if the value type is neither a string, + /// integer, boolean nor real. + void validateConstruct() const; + + /// @brief Checks if the value is accessed correctly. + /// + /// This is called from the accessors of this class. + /// + /// @param type Type of the value expected by the accessor + /// function. + /// + /// @throw InvalidOperation if the accessed value is null. + /// @throw TypeError if the expected type is not a string + /// and it doesn't match the value type. + void validateAccess(Element::types type) const; + /// @brief Name of the value. std::string name_; - /// @brief Holds value as a string. - std::string value_; + /// @brief Stored value. + ElementPtr value_; }; /// @name Definition of the multi index container for @c StampedValue. diff --git a/src/lib/cc/tests/stamped_value_unittest.cc b/src/lib/cc/tests/stamped_value_unittest.cc index 869ba41310..0fddf0d8a8 100644 --- a/src/lib/cc/tests/stamped_value_unittest.cc +++ b/src/lib/cc/tests/stamped_value_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -16,24 +16,87 @@ using namespace isc::data; namespace { +// Tests that the stamped value can be created with a NULL value. +TEST(StampedValueTest, createNull) { + StampedValuePtr value; + ASSERT_NO_THROW(value = StampedValue::create("bar")); + + EXPECT_TRUE(value->amNull()); + + EXPECT_THROW(value->getType(), InvalidOperation); + EXPECT_THROW(value->getValue(), InvalidOperation); + EXPECT_THROW(value->getSignedIntegerValue(), InvalidOperation); + EXPECT_THROW(value->getBoolValue(), InvalidOperation); + EXPECT_THROW(value->getDoubleValue(), InvalidOperation); +} + // Tests that stamped value from string can be created. TEST(StampedValueTest, createFromString) { - boost::scoped_ptr value; - ASSERT_NO_THROW(value.reset(new StampedValue("bar", "foo"))); + StampedValuePtr value; + ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create("foo"))); + EXPECT_FALSE(value->amNull()); + EXPECT_EQ(Element::string, value->getType()); EXPECT_EQ("bar", value->getName()); EXPECT_EQ("foo", value->getValue()); - EXPECT_THROW(value->getSignedIntegerValue(), BadValue); + + EXPECT_THROW(value->getSignedIntegerValue(), TypeError); + EXPECT_THROW(value->getBoolValue(), TypeError); + EXPECT_THROW(value->getDoubleValue(), TypeError); } // Tests that stamped value from integer can be created. TEST(StampedValueTest, createFromInteger) { - boost::scoped_ptr value; - ASSERT_NO_THROW(value.reset(new StampedValue("bar", 5))); + StampedValuePtr value; + ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast(5)))); + EXPECT_FALSE(value->amNull()); + EXPECT_EQ(Element::integer, value->getType()); EXPECT_EQ("bar", value->getName()); EXPECT_EQ("5", value->getValue()); int64_t signed_integer; ASSERT_NO_THROW(signed_integer = value->getSignedIntegerValue()); EXPECT_EQ(5, signed_integer); + + EXPECT_THROW(value->getBoolValue(), TypeError); + EXPECT_THROW(value->getDoubleValue(), TypeError); +} + +// Tests that stamped value from bool can be created. +TEST(StampedValueTest, createFromBool) { + StampedValuePtr value; + ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast(true)))); + EXPECT_FALSE(value->amNull()); + EXPECT_EQ(Element::boolean, value->getType()); + EXPECT_EQ("bar", value->getName()); + EXPECT_EQ("1", value->getValue()); + bool bool_value = false; + ASSERT_NO_THROW(bool_value = value->getBoolValue()); + EXPECT_TRUE(bool_value); + + EXPECT_THROW(value->getSignedIntegerValue(), TypeError); + EXPECT_THROW(value->getDoubleValue(), TypeError); +} + +// Tests that stamped value from real can be created. +TEST(StampedValueTest, createFromDouble) { + StampedValuePtr value; + ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast(1.45)))); + EXPECT_FALSE(value->amNull()); + EXPECT_EQ(Element::real, value->getType()); + EXPECT_EQ("bar", value->getName()); + EXPECT_EQ("1.45", value->getValue()); + double double_value = 0; + ASSERT_NO_THROW(double_value = value->getDoubleValue()); + EXPECT_EQ(1.45, double_value); + + EXPECT_THROW(value->getSignedIntegerValue(), TypeError); + EXPECT_THROW(value->getBoolValue(), TypeError); +} + +// Tests that the value must have an allowed type. +TEST(StampedValueTest, createFailures) { + EXPECT_THROW(StampedValue::create("bar", ElementPtr()), BadValue); + EXPECT_THROW(StampedValue::create("bar", Element::createMap()), TypeError); + EXPECT_THROW(StampedValue::create("bar", Element::createList()), TypeError); } // Tests that Elements can be created from stamped values.