Commit cd0b51a6 authored by Marcin Siodelski's avatar Marcin Siodelski

[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
// 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':
......
// 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);
}
......
// 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_);
}
}
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