diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index a06ccddcd3..d1573ca931 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -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(i)))); +Element::create(const long long int i, const Position& pos) { + return (ElementPtr(new IntElement(static_cast(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(number))); + return (Element::create(boost::lexical_cast(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(number))); + return (Element::create(boost::lexical_cast(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': diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h index bea9dbb626..80d1bb40ff 100644 --- a/src/lib/cc/data.h +++ b/src/lib/cc/data.h @@ -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(i))); }; - static ElementPtr create(const long int i) { return (create(static_cast(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(i), pos)); + }; + static ElementPtr create(const long int i, + const Position& pos = ZERO_POSITION()) { + return (create(static_cast(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 l; public: - ListElement() : Element(list) {} + ListElement(const Position& pos = ZERO_POSITION()) + : Element(list, pos) {} const std::vector& listValue() const { return (l); } using Element::getValue; bool getValue(std::vector& t) const { @@ -490,8 +560,9 @@ class MapElement : public Element { std::map 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& mapValue() const { return (m); } diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 4f1057c1e7..8e31c32a15 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -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_); + +} + }