Commit cd0b51a6 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3408] Propagate line numbers and positions of elements in Element objects

parent d1c178b9
// 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 // Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
...@@ -209,38 +209,38 @@ bool operator!=(const Element& a, const Element& b) { ...@@ -209,38 +209,38 @@ bool operator!=(const Element& a, const Element& b) {
// factory functions // factory functions
// //
ElementPtr ElementPtr
Element::create() { Element::create(const Position& pos) {
return (ElementPtr(new NullElement())); return (ElementPtr(new NullElement(pos)));
} }
ElementPtr ElementPtr
Element::create(const long long int i) { Element::create(const long long int i, const Position& pos) {
return (ElementPtr(new IntElement(static_cast<int64_t>(i)))); return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
} }
ElementPtr ElementPtr
Element::create(const double d) { Element::create(const double d, const Position& pos) {
return (ElementPtr(new DoubleElement(d))); return (ElementPtr(new DoubleElement(d, pos)));
} }
ElementPtr ElementPtr
Element::create(const std::string& s) { Element::create(const std::string& s, const Position& pos) {
return (ElementPtr(new StringElement(s))); return (ElementPtr(new StringElement(s, pos)));
} }
ElementPtr ElementPtr
Element::create(const bool b) { Element::create(const bool b, const Position& pos) {
return (ElementPtr(new BoolElement(b))); return (ElementPtr(new BoolElement(b, pos)));
} }
ElementPtr ElementPtr
Element::createList() { Element::createList(const Position& pos) {
return (ElementPtr(new ListElement())); return (ElementPtr(new ListElement(pos)));
} }
ElementPtr ElementPtr
Element::createMap() { Element::createMap(const Position& pos) {
return (ElementPtr(new MapElement())); return (ElementPtr(new MapElement(pos)));
} }
...@@ -399,49 +399,59 @@ numberFromStringstream(std::istream& in, int& pos) { ...@@ -399,49 +399,59 @@ numberFromStringstream(std::istream& in, int& pos) {
// value is larger than an int can handle) // value is larger than an int can handle)
// //
ElementPtr ElementPtr
fromStringstreamNumber(std::istream& in, int& pos) { fromStringstreamNumber(std::istream& in, const std::string& file,
std::string number = numberFromStringstream(in, pos); 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()) { if (number.find_first_of(".eE") < number.size()) {
try { 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&) { } catch (const boost::bad_lexical_cast&) {
isc_throw(JSONError, std::string("Number overflow: ") + number); isc_throw(JSONError, std::string("Number overflow: ") + number);
} }
} else { } else {
try { 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&) { } 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 ElementPtr
fromStringstreamBool(std::istream& in, const std::string& file, fromStringstreamBool(std::istream& in, const std::string& file,
const int line, int& pos) const int line, int& pos)
{ {
const uint32_t start_pos = pos;
const std::string word = wordFromStringstream(in, pos); const std::string word = wordFromStringstream(in, pos);
if (boost::iequals(word, "True")) { if (boost::iequals(word, "True")) {
return (Element::create(true)); return (Element::create(true, Element::Position(line, start_pos)));
} else if (boost::iequals(word, "False")) { } else if (boost::iequals(word, "False")) {
return (Element::create(false)); return (Element::create(false, Element::Position(line, start_pos)));
} else { } else {
throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos); throwJSONError(std::string("Bad boolean value: ") + word, file,
// above is a throw shortcurt, return empty is never reached line, start_pos);
return (ElementPtr());
} }
return (ElementPtr());
} }
ElementPtr ElementPtr
fromStringstreamNull(std::istream& in, const std::string& file, fromStringstreamNull(std::istream& in, const std::string& file,
const int line, int& pos) const int line, int& pos)
{ {
const uint32_t start_pos = pos;
const std::string word = wordFromStringstream(in, pos); const std::string word = wordFromStringstream(in, pos);
if (boost::iequals(word, "null")) { if (boost::iequals(word, "null")) {
return (Element::create()); return (Element::create(Element::Position(line, start_pos)));
} else { } else {
throwJSONError(std::string("Bad null value: ") + word, file, line, pos); throwJSONError(std::string("Bad null value: ") + word, file,
line, start_pos);
return (ElementPtr()); return (ElementPtr());
} }
} }
...@@ -450,7 +460,9 @@ ElementPtr ...@@ -450,7 +460,9 @@ ElementPtr
fromStringstreamString(std::istream& in, const std::string& file, int& line, fromStringstreamString(std::istream& in, const std::string& file, int& line,
int& pos) 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 ElementPtr
...@@ -458,7 +470,7 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line, ...@@ -458,7 +470,7 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line,
int& pos) int& pos)
{ {
int c = 0; int c = 0;
ElementPtr list = Element::createList(); ElementPtr list = Element::createList(Element::Position(line, pos));
ConstElementPtr cur_list_element; ConstElementPtr cur_list_element;
skipChars(in, WHITESPACE, line, pos); skipChars(in, WHITESPACE, line, pos);
...@@ -479,7 +491,7 @@ ElementPtr ...@@ -479,7 +491,7 @@ ElementPtr
fromStringstreamMap(std::istream& in, const std::string& file, int& line, fromStringstreamMap(std::istream& in, const std::string& file, int& line,
int& pos) int& pos)
{ {
ElementPtr map = Element::createMap(); ElementPtr map = Element::createMap(Element::Position(line, pos));
skipChars(in, WHITESPACE, line, pos); skipChars(in, WHITESPACE, line, pos);
int c = in.peek(); int c = in.peek();
if (c == EOF) { if (c == EOF) {
...@@ -594,7 +606,7 @@ Element::fromJSON(std::istream& in, const std::string& file, int& line, ...@@ -594,7 +606,7 @@ Element::fromJSON(std::istream& in, const std::string& file, int& line,
case '.': case '.':
in.putback(c); in.putback(c);
--pos; --pos;
element = fromStringstreamNumber(in, pos); element = fromStringstreamNumber(in, file, line, pos);
el_read = true; el_read = true;
break; break;
case 't': case 't':
......
// 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 // Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
...@@ -72,16 +72,56 @@ public: ...@@ -72,16 +72,56 @@ public:
/// ///
class Element { 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: private:
// technically the type could be omitted; is it useful? // technically the type could be omitted; is it useful?
// should we remove it or replace it with a pure virtual // should we remove it or replace it with a pure virtual
// function getType? // function getType?
int type; int type_;
/// \brief Position of the element in the configuration string.
Position position_;
protected: 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: public:
// any is a special type used in list specifications, specifying // any is a special type used in list specifications, specifying
// that the elements can be of any type // that the elements can be of any type
enum types { integer, real, boolean, null, string, list, map, any }; enum types { integer, real, boolean, null, string, list, map, any };
...@@ -89,7 +129,11 @@ public: ...@@ -89,7 +129,11 @@ public:
virtual ~Element() {}; virtual ~Element() {};
/// \return the type of this 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 /// Returns a string representing the Element and all its
/// child elements; note that this is different from stringValue(), /// child elements; note that this is different from stringValue(),
...@@ -282,22 +326,42 @@ public: ...@@ -282,22 +326,42 @@ public:
/// Notes: Read notes of IntElement definition about the use of /// Notes: Read notes of IntElement definition about the use of
/// long long int, long int and int. /// long long int, long int and int.
//@{ //@{
static ElementPtr create(); static ElementPtr create(const Position& pos = ZERO_POSITION());
static ElementPtr create(const long long int i); static ElementPtr create(const long long int i,
static ElementPtr create(const int i) { return (create(static_cast<long long int>(i))); }; const Position& pos = ZERO_POSITION());
static ElementPtr create(const long int i) { return (create(static_cast<long long int>(i))); }; static ElementPtr create(const int i,
static ElementPtr create(const double d); const Position& pos = ZERO_POSITION()) {
static ElementPtr create(const bool b); return (create(static_cast<long long int>(i), pos));
static ElementPtr create(const std::string& s); };
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 // need both std:string and char *, since c++ will match
// bool before std::string when you pass it a char * // 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. /// \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. /// \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: ...@@ -386,7 +450,7 @@ public:
/// (C++ tries to convert integer type values and reference/pointer /// (C++ tries to convert integer type values and reference/pointer
/// if value types do not match exactly) /// if value types do not match exactly)
/// We decided the storage as int64_t, /// 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. /// and cast int/long/long long to int64_t via long long.
/// Therefore, call by value methods (create, setValue) have three /// Therefore, call by value methods (create, setValue) have three
/// (int,long,long long) definitions. Others use int64_t. /// (int,long,long long) definitions. Others use int64_t.
...@@ -396,7 +460,8 @@ class IntElement : public Element { ...@@ -396,7 +460,8 @@ class IntElement : public Element {
private: private:
public: 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); } int64_t intValue() const { return (i); }
using Element::getValue; using Element::getValue;
bool getValue(int64_t& t) const { t = i; return (true); } bool getValue(int64_t& t) const { t = i; return (true); }
...@@ -410,7 +475,8 @@ class DoubleElement : public Element { ...@@ -410,7 +475,8 @@ class DoubleElement : public Element {
double d; double d;
public: 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); } double doubleValue() const { return (d); }
using Element::getValue; using Element::getValue;
bool getValue(double& t) const { t = d; return (true); } bool getValue(double& t) const { t = d; return (true); }
...@@ -424,7 +490,8 @@ class BoolElement : public Element { ...@@ -424,7 +490,8 @@ class BoolElement : public Element {
bool b; bool b;
public: 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); } bool boolValue() const { return (b); }
using Element::getValue; using Element::getValue;
bool getValue(bool& t) const { t = b; return (true); } bool getValue(bool& t) const { t = b; return (true); }
...@@ -436,7 +503,8 @@ public: ...@@ -436,7 +503,8 @@ public:
class NullElement : public Element { class NullElement : public Element {
public: public:
NullElement() : Element(null) {}; NullElement(const Position& pos = ZERO_POSITION())
: Element(null, pos) {};
void toJSON(std::ostream& ss) const; void toJSON(std::ostream& ss) const;
bool equals(const Element& other) const; bool equals(const Element& other) const;
}; };
...@@ -445,7 +513,8 @@ class StringElement : public Element { ...@@ -445,7 +513,8 @@ class StringElement : public Element {
std::string s; std::string s;
public: 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); } std::string stringValue() const { return (s); }
using Element::getValue; using Element::getValue;
bool getValue(std::string& t) const { t = s; return (true); } bool getValue(std::string& t) const { t = s; return (true); }
...@@ -459,7 +528,8 @@ class ListElement : public Element { ...@@ -459,7 +528,8 @@ class ListElement : public Element {
std::vector<ConstElementPtr> l; std::vector<ConstElementPtr> l;
public: public:
ListElement() : Element(list) {} ListElement(const Position& pos = ZERO_POSITION())
: Element(list, pos) {}
const std::vector<ConstElementPtr>& listValue() const { return (l); } const std::vector<ConstElementPtr>& listValue() const { return (l); }
using Element::getValue; using Element::getValue;
bool getValue(std::vector<ConstElementPtr>& t) const { bool getValue(std::vector<ConstElementPtr>& t) const {
...@@ -490,8 +560,9 @@ class MapElement : public Element { ...@@ -490,8 +560,9 @@ class MapElement : public Element {
std::map<std::string, ConstElementPtr> m; std::map<std::string, ConstElementPtr> m;
public: public:
MapElement() : Element(map) {} MapElement(const Position& pos = ZERO_POSITION()) : Element(map, pos) {}
// TODO: should we have direct iterators instead of exposing the std::map here? // @todo should we have direct iterators instead of exposing the std::map
// here?
const std::map<std::string, ConstElementPtr>& mapValue() const { const std::map<std::string, ConstElementPtr>& mapValue() const {
return (m); return (m);
} }
......
// 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 // Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
...@@ -929,4 +929,87 @@ TEST(Element, merge) { ...@@ -929,4 +929,87 @@ TEST(Element, merge) {
EXPECT_EQ(*b, *c); 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_);
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment