cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
StampedValue::create("name1", "value1"));
cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
- StampedValue::create("name2", 65));
+ StampedValue::create("name2",
+ Element::create(static_cast<int64_t>(65))));
cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
StampedValue::create("name3", "value3"));
cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
value);
- value = StampedValue::create("name2", 65);
+ value = StampedValue::create("name2", Element::create(static_cast<int64_t>(65)));
value->setModificationTime(timestamps_["today"]);
cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
value);
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<std::string>(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<Element::types>(value_->getType())) {
+ case Element::string:
+ return (value_->stringValue());
+ case Element::integer:
+ return (boost::lexical_cast<std::string>(value_->intValue()));
+ case Element::boolean:
+ return (value_->boolValue() ? "1" : "0");
+ case Element::real:
+ return (boost::lexical_cast<std::string>(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<int64_t>(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<Element::types>(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<Element::types>(value_->getType()))
+ << " type");
+ }
}
ElementPtr
-// 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
/// @brief Pointer to the stamped value.
typedef boost::shared_ptr<StampedValue> 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.
///
/// @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
/// 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.
-// 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
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<StampedValue> 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<StampedValue> value;
- ASSERT_NO_THROW(value.reset(new StampedValue("bar", 5)));
+ StampedValuePtr value;
+ ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast<int64_t>(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<bool>(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<double>(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.