mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-05 16:35:23 +00:00
[3408] Propagate line numbers and positions of elements in Element objects
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2010, 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -209,38 +209,38 @@ bool operator!=(const Element& a, const Element& b) {
|
||||
// factory functions
|
||||
//
|
||||
ElementPtr
|
||||
Element::create() {
|
||||
return (ElementPtr(new NullElement()));
|
||||
Element::create(const Position& pos) {
|
||||
return (ElementPtr(new NullElement(pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::create(const long long int i) {
|
||||
return (ElementPtr(new IntElement(static_cast<int64_t>(i))));
|
||||
Element::create(const long long int i, const Position& pos) {
|
||||
return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::create(const double d) {
|
||||
return (ElementPtr(new DoubleElement(d)));
|
||||
Element::create(const double d, const Position& pos) {
|
||||
return (ElementPtr(new DoubleElement(d, pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::create(const std::string& s) {
|
||||
return (ElementPtr(new StringElement(s)));
|
||||
Element::create(const std::string& s, const Position& pos) {
|
||||
return (ElementPtr(new StringElement(s, pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::create(const bool b) {
|
||||
return (ElementPtr(new BoolElement(b)));
|
||||
Element::create(const bool b, const Position& pos) {
|
||||
return (ElementPtr(new BoolElement(b, pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::createList() {
|
||||
return (ElementPtr(new ListElement()));
|
||||
Element::createList(const Position& pos) {
|
||||
return (ElementPtr(new ListElement(pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::createMap() {
|
||||
return (ElementPtr(new MapElement()));
|
||||
Element::createMap(const Position& pos) {
|
||||
return (ElementPtr(new MapElement(pos)));
|
||||
}
|
||||
|
||||
|
||||
@@ -399,49 +399,59 @@ numberFromStringstream(std::istream& in, int& pos) {
|
||||
// value is larger than an int can handle)
|
||||
//
|
||||
ElementPtr
|
||||
fromStringstreamNumber(std::istream& in, int& pos) {
|
||||
std::string number = numberFromStringstream(in, pos);
|
||||
fromStringstreamNumber(std::istream& in, const std::string& file,
|
||||
const int& line, int& pos) {
|
||||
const uint32_t start_pos = pos;
|
||||
const std::string number = numberFromStringstream(in, pos);
|
||||
|
||||
if (number.find_first_of(".eE") < number.size()) {
|
||||
try {
|
||||
return (Element::create(boost::lexical_cast<double>(number)));
|
||||
return (Element::create(boost::lexical_cast<double>(number),
|
||||
Element::Position(line, start_pos)));
|
||||
} catch (const boost::bad_lexical_cast&) {
|
||||
isc_throw(JSONError, std::string("Number overflow: ") + number);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return (Element::create(boost::lexical_cast<int64_t>(number)));
|
||||
return (Element::create(boost::lexical_cast<int64_t>(number),
|
||||
Element::Position(line, start_pos)));
|
||||
} catch (const boost::bad_lexical_cast&) {
|
||||
isc_throw(JSONError, std::string("Number overflow: ") + number);
|
||||
throwJSONError(std::string("Number overflow: ") + number, file,
|
||||
line, start_pos);
|
||||
}
|
||||
}
|
||||
return (ElementPtr());
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
fromStringstreamBool(std::istream& in, const std::string& file,
|
||||
const int line, int& pos)
|
||||
{
|
||||
const uint32_t start_pos = pos;
|
||||
const std::string word = wordFromStringstream(in, pos);
|
||||
|
||||
if (boost::iequals(word, "True")) {
|
||||
return (Element::create(true));
|
||||
return (Element::create(true, Element::Position(line, start_pos)));
|
||||
} else if (boost::iequals(word, "False")) {
|
||||
return (Element::create(false));
|
||||
return (Element::create(false, Element::Position(line, start_pos)));
|
||||
} else {
|
||||
throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
|
||||
// above is a throw shortcurt, return empty is never reached
|
||||
return (ElementPtr());
|
||||
throwJSONError(std::string("Bad boolean value: ") + word, file,
|
||||
line, start_pos);
|
||||
}
|
||||
return (ElementPtr());
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
fromStringstreamNull(std::istream& in, const std::string& file,
|
||||
const int line, int& pos)
|
||||
{
|
||||
const uint32_t start_pos = pos;
|
||||
const std::string word = wordFromStringstream(in, pos);
|
||||
if (boost::iequals(word, "null")) {
|
||||
return (Element::create());
|
||||
return (Element::create(Element::Position(line, start_pos)));
|
||||
} else {
|
||||
throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
|
||||
throwJSONError(std::string("Bad null value: ") + word, file,
|
||||
line, start_pos);
|
||||
return (ElementPtr());
|
||||
}
|
||||
}
|
||||
@@ -450,7 +460,9 @@ ElementPtr
|
||||
fromStringstreamString(std::istream& in, const std::string& file, int& line,
|
||||
int& pos)
|
||||
{
|
||||
return (Element::create(strFromStringstream(in, file, line, pos)));
|
||||
const uint32_t start_pos = pos;
|
||||
const std::string string_value = strFromStringstream(in, file, line, pos);
|
||||
return (Element::create(string_value, Element::Position(line, start_pos)));
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
@@ -458,7 +470,7 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line,
|
||||
int& pos)
|
||||
{
|
||||
int c = 0;
|
||||
ElementPtr list = Element::createList();
|
||||
ElementPtr list = Element::createList(Element::Position(line, pos));
|
||||
ConstElementPtr cur_list_element;
|
||||
|
||||
skipChars(in, WHITESPACE, line, pos);
|
||||
@@ -479,7 +491,7 @@ ElementPtr
|
||||
fromStringstreamMap(std::istream& in, const std::string& file, int& line,
|
||||
int& pos)
|
||||
{
|
||||
ElementPtr map = Element::createMap();
|
||||
ElementPtr map = Element::createMap(Element::Position(line, pos));
|
||||
skipChars(in, WHITESPACE, line, pos);
|
||||
int c = in.peek();
|
||||
if (c == EOF) {
|
||||
@@ -594,7 +606,7 @@ Element::fromJSON(std::istream& in, const std::string& file, int& line,
|
||||
case '.':
|
||||
in.putback(c);
|
||||
--pos;
|
||||
element = fromStringstreamNumber(in, pos);
|
||||
element = fromStringstreamNumber(in, file, line, pos);
|
||||
el_read = true;
|
||||
break;
|
||||
case 't':
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2010, 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -72,16 +72,56 @@ public:
|
||||
///
|
||||
class Element {
|
||||
|
||||
public:
|
||||
/// \brief Represents the position of the data element within a
|
||||
/// configuration string.
|
||||
struct Position {
|
||||
uint32_t line_; ///< Line number.
|
||||
uint32_t pos_; ///< Position within the line.
|
||||
|
||||
/// \brief Constructor.
|
||||
///
|
||||
/// \param line Line number.
|
||||
/// \param pos Position within the line.
|
||||
Position(const uint32_t line, const uint32_t pos)
|
||||
: line_(line), pos_(pos) {
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Returns @c Position object with line_ and pos_ set to 0.
|
||||
///
|
||||
/// The object containing two zeros is a default for most of the
|
||||
/// methods creating @c Element objects.
|
||||
static const Position& ZERO_POSITION() {
|
||||
static Position position(0, 0);
|
||||
return (position);
|
||||
}
|
||||
|
||||
private:
|
||||
// technically the type could be omitted; is it useful?
|
||||
// should we remove it or replace it with a pure virtual
|
||||
// function getType?
|
||||
int type;
|
||||
int type_;
|
||||
|
||||
/// \brief Position of the element in the configuration string.
|
||||
Position position_;
|
||||
|
||||
protected:
|
||||
Element(int t) { type = t; }
|
||||
|
||||
/// \brief Constructor.
|
||||
///
|
||||
/// \param t Element type.
|
||||
/// \param line Line number in the configuration string where this element
|
||||
/// starts. It is used to communicate the broken parts of configuration
|
||||
/// through logging mechanism.
|
||||
/// \param line_pos Position within the line of the configuration string
|
||||
/// where this element's value starts.
|
||||
Element(int t, const Position& pos = ZERO_POSITION())
|
||||
: type_(t), position_(pos) {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// any is a special type used in list specifications, specifying
|
||||
// that the elements can be of any type
|
||||
enum types { integer, real, boolean, null, string, list, map, any };
|
||||
@@ -89,7 +129,11 @@ public:
|
||||
virtual ~Element() {};
|
||||
|
||||
/// \return the type of this element
|
||||
int getType() const { return (type); }
|
||||
int getType() const { return (type_); }
|
||||
|
||||
/// \brief Returns line number where the data element's value starts in a
|
||||
/// configuration string
|
||||
const Position& getPosition() const { return (position_); }
|
||||
|
||||
/// Returns a string representing the Element and all its
|
||||
/// child elements; note that this is different from stringValue(),
|
||||
@@ -282,22 +326,42 @@ public:
|
||||
/// Notes: Read notes of IntElement definition about the use of
|
||||
/// long long int, long int and int.
|
||||
//@{
|
||||
static ElementPtr create();
|
||||
static ElementPtr create(const long long int i);
|
||||
static ElementPtr create(const int i) { return (create(static_cast<long long int>(i))); };
|
||||
static ElementPtr create(const long int i) { return (create(static_cast<long long int>(i))); };
|
||||
static ElementPtr create(const double d);
|
||||
static ElementPtr create(const bool b);
|
||||
static ElementPtr create(const std::string& s);
|
||||
static ElementPtr create(const Position& pos = ZERO_POSITION());
|
||||
static ElementPtr create(const long long int i,
|
||||
const Position& pos = ZERO_POSITION());
|
||||
static ElementPtr create(const int i,
|
||||
const Position& pos = ZERO_POSITION()) {
|
||||
return (create(static_cast<long long int>(i), pos));
|
||||
};
|
||||
static ElementPtr create(const long int i,
|
||||
const Position& pos = ZERO_POSITION()) {
|
||||
return (create(static_cast<long long int>(i), pos));
|
||||
};
|
||||
static ElementPtr create(const double d,
|
||||
const Position& pos = ZERO_POSITION());
|
||||
|
||||
static ElementPtr create(const bool b,
|
||||
const Position& pos = ZERO_POSITION());
|
||||
static ElementPtr create(const std::string& s,
|
||||
const Position& pos = ZERO_POSITION());
|
||||
// need both std:string and char *, since c++ will match
|
||||
// bool before std::string when you pass it a char *
|
||||
static ElementPtr create(const char *s) { return (create(std::string(s))); }
|
||||
static ElementPtr create(const char *s,
|
||||
const Position& pos = ZERO_POSITION()) {
|
||||
return (create(std::string(s), pos));
|
||||
}
|
||||
|
||||
/// \brief Creates an empty ListElement type ElementPtr.
|
||||
static ElementPtr createList();
|
||||
///
|
||||
/// \param line_num Line number in the configuration string where the
|
||||
/// data element is located.
|
||||
static ElementPtr createList(const Position& pos = ZERO_POSITION());
|
||||
|
||||
/// \brief Creates an empty MapElement type ElementPtr.
|
||||
static ElementPtr createMap();
|
||||
///
|
||||
/// \param line_num Line number in the configuration string where the
|
||||
/// data element is located.
|
||||
static ElementPtr createMap(const Position& pos = ZERO_POSITION());
|
||||
//@}
|
||||
|
||||
|
||||
@@ -386,7 +450,7 @@ public:
|
||||
/// (C++ tries to convert integer type values and reference/pointer
|
||||
/// if value types do not match exactly)
|
||||
/// We decided the storage as int64_t,
|
||||
/// three (long long, long, int) override function defintions
|
||||
/// three (long long, long, int) override function defintions
|
||||
/// and cast int/long/long long to int64_t via long long.
|
||||
/// Therefore, call by value methods (create, setValue) have three
|
||||
/// (int,long,long long) definitions. Others use int64_t.
|
||||
@@ -396,7 +460,8 @@ class IntElement : public Element {
|
||||
private:
|
||||
|
||||
public:
|
||||
IntElement(int64_t v) : Element(integer), i(v) { }
|
||||
IntElement(int64_t v, const Position& pos = ZERO_POSITION())
|
||||
: Element(integer, pos), i(v) { }
|
||||
int64_t intValue() const { return (i); }
|
||||
using Element::getValue;
|
||||
bool getValue(int64_t& t) const { t = i; return (true); }
|
||||
@@ -410,7 +475,8 @@ class DoubleElement : public Element {
|
||||
double d;
|
||||
|
||||
public:
|
||||
DoubleElement(double v) : Element(real), d(v) {};
|
||||
DoubleElement(double v, const Position& pos = ZERO_POSITION())
|
||||
: Element(real, pos), d(v) {};
|
||||
double doubleValue() const { return (d); }
|
||||
using Element::getValue;
|
||||
bool getValue(double& t) const { t = d; return (true); }
|
||||
@@ -424,7 +490,8 @@ class BoolElement : public Element {
|
||||
bool b;
|
||||
|
||||
public:
|
||||
BoolElement(const bool v) : Element(boolean), b(v) {};
|
||||
BoolElement(const bool v, const Position& pos = ZERO_POSITION())
|
||||
: Element(boolean, pos), b(v) {};
|
||||
bool boolValue() const { return (b); }
|
||||
using Element::getValue;
|
||||
bool getValue(bool& t) const { t = b; return (true); }
|
||||
@@ -436,7 +503,8 @@ public:
|
||||
|
||||
class NullElement : public Element {
|
||||
public:
|
||||
NullElement() : Element(null) {};
|
||||
NullElement(const Position& pos = ZERO_POSITION())
|
||||
: Element(null, pos) {};
|
||||
void toJSON(std::ostream& ss) const;
|
||||
bool equals(const Element& other) const;
|
||||
};
|
||||
@@ -445,7 +513,8 @@ class StringElement : public Element {
|
||||
std::string s;
|
||||
|
||||
public:
|
||||
StringElement(std::string v) : Element(string), s(v) {};
|
||||
StringElement(std::string v, const Position& pos = ZERO_POSITION())
|
||||
: Element(string, pos), s(v) {};
|
||||
std::string stringValue() const { return (s); }
|
||||
using Element::getValue;
|
||||
bool getValue(std::string& t) const { t = s; return (true); }
|
||||
@@ -459,7 +528,8 @@ class ListElement : public Element {
|
||||
std::vector<ConstElementPtr> l;
|
||||
|
||||
public:
|
||||
ListElement() : Element(list) {}
|
||||
ListElement(const Position& pos = ZERO_POSITION())
|
||||
: Element(list, pos) {}
|
||||
const std::vector<ConstElementPtr>& listValue() const { return (l); }
|
||||
using Element::getValue;
|
||||
bool getValue(std::vector<ConstElementPtr>& t) const {
|
||||
@@ -490,8 +560,9 @@ class MapElement : public Element {
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
public:
|
||||
MapElement() : Element(map) {}
|
||||
// TODO: should we have direct iterators instead of exposing the std::map here?
|
||||
MapElement(const Position& pos = ZERO_POSITION()) : Element(map, pos) {}
|
||||
// @todo should we have direct iterators instead of exposing the std::map
|
||||
// here?
|
||||
const std::map<std::string, ConstElementPtr>& mapValue() const {
|
||||
return (m);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2009, 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -929,4 +929,87 @@ TEST(Element, merge) {
|
||||
EXPECT_EQ(*b, *c);
|
||||
|
||||
}
|
||||
|
||||
TEST(Element, getPosition) {
|
||||
// Create a JSON string holding different type of values. Some of the
|
||||
// values in the config string are not aligned, so as we can check that
|
||||
// the position is set correctly for the elements.
|
||||
ElementPtr top = Element::fromJSON("{\n"
|
||||
" \"a\": 2,\n"
|
||||
" \"b\":true,\n"
|
||||
" \"cy\": \"a string\",\n"
|
||||
" \"dyz\": {\n"
|
||||
"\n"
|
||||
" \"e\": 3,\n"
|
||||
" \"f\": null\n"
|
||||
"\n"
|
||||
" },\n"
|
||||
" \"g\": [ 5, 6,\n"
|
||||
" 7 ]\n"
|
||||
"}\n");
|
||||
ASSERT_TRUE(top);
|
||||
|
||||
// Element "a"
|
||||
ConstElementPtr level1_el = top->get("a");
|
||||
ASSERT_TRUE(level1_el);
|
||||
EXPECT_EQ(2, level1_el->getPosition().line_);
|
||||
EXPECT_EQ(11, level1_el->getPosition().pos_);
|
||||
|
||||
// Element "b"
|
||||
level1_el = top->get("b");
|
||||
ASSERT_TRUE(level1_el);
|
||||
EXPECT_EQ(3, level1_el->getPosition().line_);
|
||||
EXPECT_EQ(9, level1_el->getPosition().pos_);
|
||||
|
||||
// Element "cy"
|
||||
level1_el = top->get("cy");
|
||||
ASSERT_TRUE(level1_el);
|
||||
EXPECT_EQ(4, level1_el->getPosition().line_);
|
||||
EXPECT_EQ(11, level1_el->getPosition().pos_);
|
||||
|
||||
// Element "dyz"
|
||||
level1_el = top->get("dyz");
|
||||
ASSERT_TRUE(level1_el);
|
||||
EXPECT_EQ(5, level1_el->getPosition().line_);
|
||||
EXPECT_EQ(13, level1_el->getPosition().pos_);
|
||||
|
||||
// Element "e" is a sub element of "dyz".
|
||||
ConstElementPtr level2_el = level1_el->get("e");
|
||||
ASSERT_TRUE(level2_el);
|
||||
EXPECT_EQ(7, level2_el->getPosition().line_);
|
||||
EXPECT_EQ(12, level2_el->getPosition().pos_);
|
||||
|
||||
// Element "f" is also a sub element of "dyz"
|
||||
level2_el = level1_el->get("f");
|
||||
ASSERT_TRUE(level2_el);
|
||||
EXPECT_EQ(8, level2_el->getPosition().line_);
|
||||
EXPECT_EQ(14, level2_el->getPosition().pos_);
|
||||
|
||||
// Element "g" is a list.
|
||||
level1_el = top->get("g");
|
||||
ASSERT_TRUE(level1_el);
|
||||
EXPECT_EQ(11, level1_el->getPosition().line_);
|
||||
// Position indicates where the values start (excluding the "[" character)"
|
||||
EXPECT_EQ(11, level1_el->getPosition().pos_);
|
||||
|
||||
// First element from the list.
|
||||
level2_el = level1_el->get(0);
|
||||
ASSERT_TRUE(level2_el);
|
||||
EXPECT_EQ(11, level2_el->getPosition().line_);
|
||||
EXPECT_EQ(12, level2_el->getPosition().pos_);
|
||||
|
||||
// Second element from the list.
|
||||
level2_el = level1_el->get(1);
|
||||
ASSERT_TRUE(level2_el);
|
||||
EXPECT_EQ(11, level2_el->getPosition().line_);
|
||||
EXPECT_EQ(15, level2_el->getPosition().pos_);
|
||||
|
||||
// Third element from the list.
|
||||
level2_el = level1_el->get(2);
|
||||
ASSERT_TRUE(level2_el);
|
||||
EXPECT_EQ(12, level2_el->getPosition().line_);
|
||||
EXPECT_EQ(14, level2_el->getPosition().pos_);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user