namespace isc {
namespace data {
+void
+SimpleParser::checkRequired(const SimpleRequiredKeywords& required,
+ ConstElementPtr scope) {
+ for (auto name : required) {
+ if (scope->contains(name)) {
+ continue;
+ }
+ isc_throw(DhcpConfigError, "missing '" << name << "' parameter");
+ }
+}
+
+void
+SimpleParser::checkKeywords(const SimpleKeywords& keywords,
+ ConstElementPtr scope) {
+ string spurious;
+ for (auto entry : scope->mapValue()) {
+ if (keywords.count(entry.first) == 0) {
+ if (spurious.empty()) {
+ spurious = entry.first;
+ }
+ continue;
+ }
+ Element::types expected = keywords.at(entry.first);
+ if (entry.second->getType() == expected) {
+ continue;
+ }
+ isc_throw(DhcpConfigError, "'" << entry.first << "' parameter is not "
+ << (expected == Element::integer ? "an " : "a ")
+ << Element::typeToName(expected));
+ }
+ if (!spurious.empty()) {
+ isc_throw(DhcpConfigError, "spurious '" << spurious << "' parameter");
+ }
+}
+
std::string
-SimpleParser::getString(isc::data::ConstElementPtr scope, const std::string& name) {
+SimpleParser::getString(ConstElementPtr scope, const std::string& name) {
ConstElementPtr x = scope->get(name);
if (!x) {
isc_throw(DhcpConfigError,
}
int64_t
-SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name) {
+SimpleParser::getInteger(ConstElementPtr scope, const std::string& name) {
ConstElementPtr x = scope->get(name);
if (!x) {
isc_throw(DhcpConfigError,
}
bool
-SimpleParser::getBoolean(isc::data::ConstElementPtr scope, const std::string& name) {
+SimpleParser::getBoolean(ConstElementPtr scope, const std::string& name) {
ConstElementPtr x = scope->get(name);
if (!x) {
isc_throw(DhcpConfigError,
}
double
-SimpleParser::getDouble(const isc::data::ConstElementPtr& scope,
+SimpleParser::getDouble(const ConstElementPtr& scope,
const std::string& name) {
ConstElementPtr x = scope->get(name);
if (!x) {
return (elem->getPosition());
}
-size_t SimpleParser::setDefaults(isc::data::ElementPtr scope,
+size_t SimpleParser::setDefaults(ElementPtr scope,
const SimpleDefaults& default_values) {
size_t cnt = 0;
}
size_t
-SimpleParser::setListDefaults(isc::data::ConstElementPtr list,
+SimpleParser::setListDefaults(ConstElementPtr list,
const SimpleDefaults& default_values) {
size_t cnt = 0;
BOOST_FOREACH(ElementPtr entry, list->listValue()) {
}
size_t
-SimpleParser::deriveParams(isc::data::ConstElementPtr parent,
- isc::data::ElementPtr child,
+SimpleParser::deriveParams(ConstElementPtr parent,
+ ElementPtr child,
const ParamsList& params) {
if ( (parent->getType() != Element::map) ||
(child->getType() != Element::map)) {
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <cc/dhcp_config_error.h>
+#include <map>
#include <vector>
#include <string>
#include <stdint.h>
namespace isc {
namespace data {
-/// This array defines a single entry of default values
+/// This array defines a single entry of default values.
struct SimpleDefault {
SimpleDefault(const char* name, isc::data::Element::types type, const char* value)
:name_(name), type_(type), value_(value) {}
const char* value_;
};
-/// This specifies all default values in a given scope (e.g. a subnet)
+/// This specifies all required keywords.
+typedef std::vector<std::string> SimpleRequiredKeywords;
+
+/// This specifies all accepted keywords with their types.
+typedef std::map<std::string, isc::data::Element::types> SimpleKeywords;
+
+/// This specifies all default values in a given scope (e.g. a subnet).
typedef std::vector<SimpleDefault> SimpleDefaults;
/// This defines a list of all parameters that are derived (or inherited) between
-/// contexts
+/// contexts.
typedef std::vector<std::string> ParamsList;
class SimpleParser {
public:
+ /// @brief Checks that all required keywords are present.
+ ///
+ /// This method throws an exception when a required
+ /// entry is not present un the given scope.
+ ///
+ /// @param required Required keywords.
+ /// @param scope Specified parameters which are checked.
+ /// @throw DhcpConfigError if a required parameter is not present.
+ static void checkRequired(const SimpleRequiredKeywords& required,
+ isc::data::ConstElementPtr scope);
+
+ /// @brief Checks acceptable keywords with their expected type.
+ ///
+ /// This methods throws an exception when a not acceptable keyword
+ /// is found or when an acceptable entry does not have the expected type.
+ ///
+ /// @param keywords The @c SimpleKeywords keywords and types map.
+ /// @param scope Specified parameters which are checked.
+ /// @throw DhcpConfigError if a not acceptable keyword is found.
+ /// @throw DhcpConfigError if an acceptable entry does not have
+ /// the expected type.
+ static void checkKeywords(const SimpleKeywords& keywords,
+ isc::data::ConstElementPtr scope);
+
/// @brief Derives (inherits) parameters from parent scope to a child
///
/// This method derives parameters from the parent scope to the child,
using namespace isc::asiolink;
using isc::dhcp::DhcpConfigError;
+/// This list defines required keywords.
+const SimpleRequiredKeywords REQUIRED_KEYWORDS = { "foobar" };
+
+/// This table defines keywords and types.
+const SimpleKeywords KEYWORDS = {
+ { "id", Element::integer },
+ { "prefix", Element::string },
+ { "map", Element::map }
+};
+
/// This table defines sample default values. Although these are DHCPv6
/// specific, the mechanism is generic and can be used by any other component.
const SimpleDefaults SAMPLE_DEFAULTS = {
}
};
+// This test checks if the checkRequired method works as expected.
+TEST_F(SimpleParserTest, checkRequired) {
+ ConstElementPtr empty = Element::fromJSON("{ }");
+ EXPECT_THROW(SimpleParser::checkRequired(REQUIRED_KEYWORDS, empty),
+ DhcpConfigError);
+ ConstElementPtr other = Element::fromJSON("{ \"foo\": 1, \"bar\": 2 }");
+ EXPECT_THROW(SimpleParser::checkRequired(REQUIRED_KEYWORDS, other),
+ DhcpConfigError);
+ ConstElementPtr good = Element::fromJSON("{ \"foobar\": 2 }");
+ EXPECT_NO_THROW(SimpleParser::checkRequired(REQUIRED_KEYWORDS, good));
+}
+
+// This test checks if the checkKeywords method works as expected.
+TEST_F(SimpleParserTest, checkKeywords) {
+ ConstElementPtr empty = Element::fromJSON("{ }");
+ EXPECT_NO_THROW(SimpleParser::checkKeywords(KEYWORDS, empty));
+ ConstElementPtr id = Element::fromJSON("{ \"id\": 1 }");
+ EXPECT_NO_THROW(SimpleParser::checkKeywords(KEYWORDS, id));
+ ConstElementPtr bad_id = Element::fromJSON("{ \"id\": true }");
+ EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, bad_id),
+ DhcpConfigError);
+ ConstElementPtr bad_prefix = Element::fromJSON("{ \"prefix\": 12 }");
+ EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, bad_prefix),
+ DhcpConfigError);
+ ConstElementPtr bad_map = Element::fromJSON("{ \"map\": [ ] }");
+ EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, bad_map),
+ DhcpConfigError);
+ ConstElementPtr spurious = Element::fromJSON("{ \"spurious\": 1 }");
+ EXPECT_THROW(SimpleParser::checkKeywords(KEYWORDS, spurious),
+ DhcpConfigError);
+
+ // Bad type has precedence.
+ ConstElementPtr bad = Element::fromJSON("{ \"spurious\": 1, \"id\": true }");
+ try {
+ SimpleParser::checkKeywords(KEYWORDS, bad);
+ ADD_FAILURE() << "expect exception";
+ } catch (const DhcpConfigError& ex) {
+ EXPECT_EQ("'id' parameter is not an integer", std::string(ex.what()));
+ } catch (...) {
+ ADD_FAILURE() << "expect DhcpConfigError";
+ }
+}
+
// This test checks if the parameters can be inherited from the global
// scope to the subnet scope.
TEST_F(SimpleParserTest, deriveParams) {