Browse Source

[master] Merge branch 'trac3408'

Marcin Siodelski 11 years ago
parent
commit
115a52a671
3 changed files with 269 additions and 58 deletions
  1. 72 32
      src/lib/cc/data.cc
  2. 113 25
      src/lib/cc/data.h
  3. 84 1
      src/lib/cc/tests/data_unittests.cc

+ 72 - 32
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,53 @@ 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 int i, const Position& pos) {
+    return (create(static_cast<long long int>(i), pos));
+};
+
+ElementPtr
+Element::create(const long int i, const Position& pos) {
+    return (create(static_cast<long long int>(i), pos));
+};
+
+ElementPtr
+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 bool b, const Position& pos) {
+    return (ElementPtr(new BoolElement(b, pos)));
 }
 
 ElementPtr
-Element::create(const bool b) {
-    return (ElementPtr(new BoolElement(b)));
+Element::create(const std::string& s, const Position& pos) {
+    return (ElementPtr(new StringElement(s, pos)));
 }
 
 ElementPtr
-Element::createList() {
-    return (ElementPtr(new ListElement()));
+Element::create(const char *s, const Position& pos) {
+    return (create(std::string(s), pos));
 }
 
 ElementPtr
-Element::createMap() {
-    return (ElementPtr(new MapElement()));
+Element::createList(const Position& pos) {
+    return (ElementPtr(new ListElement(pos)));
+}
+
+ElementPtr
+Element::createMap(const Position& pos) {
+    return (ElementPtr(new MapElement(pos)));
 }
 
 
@@ -399,49 +414,69 @@ 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) {
+    // Remember position where the value starts. It will be set in the
+    // Position structure of the Element to be created.
+    const uint32_t start_pos = pos;
+    // This will move the pos to the end of the value.
+    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);
+            throwJSONError(std::string("Number overflow: ") + number,
+                           file, line, start_pos);
         }
     } 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)
 {
+    // Remember position where the value starts. It will be set in the
+    // Position structure of the Element to be created.
+    const uint32_t start_pos = pos;
+    // This will move the pos to the end of the value.
     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)
 {
+    // Remember position where the value starts. It will be set in the
+    // Position structure of the Element to be created.
+    const uint32_t start_pos = pos;
+    // This will move the pos to the end of the value.
     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 +485,12 @@ ElementPtr
 fromStringstreamString(std::istream& in, const std::string& file, int& line,
                        int& pos)
 {
-    return (Element::create(strFromStringstream(in, file, line, pos)));
+    // Remember position where the value starts. It will be set in the
+    // Position structure of the Element to be created.
+    const uint32_t start_pos = pos;
+    // This will move the pos to the end of the value.
+    const std::string string_value = strFromStringstream(in, file, line, pos);
+    return (Element::create(string_value, Element::Position(line, start_pos)));
 }
 
 ElementPtr
@@ -458,7 +498,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 +519,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 +634,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':

+ 113 - 25
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
@@ -15,6 +15,7 @@
 #ifndef ISC_DATA_H
 #define ISC_DATA_H 1
 
+#include <stdint.h>
 #include <string>
 #include <vector>
 #include <map>
@@ -72,16 +73,73 @@ public:
 ///
 class Element {
 
+public:
+    /// \brief Represents the position of the data element within a
+    /// configuration string.
+    ///
+    /// Position comprises a line number and an offset within this line
+    /// where the element value starts. For example, if the JSON string is
+    ///
+    /// \code
+    /// { "foo": "some string",
+    ///   "bar": 123 }
+    /// \endcode
+    ///
+    /// the position of the element "bar" is: line_ = 2; pos_ = 9, because
+    /// begining of the value "123" is at offset 9 from the beginning of
+    /// the second line, including whitespaces.
+    ///
+    /// Note that the @c Position structure is used as an argument to @c Element
+    /// constructors and factory functions to avoid ambiguity and so that the
+    /// uint32_t arguments holding line number and position within the line are
+    /// not confused with the @c Element values passed to these functions.
+    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. The returned value is static
+    /// so as it is not created everytime the function with the default
+    /// position argument is called.
+    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 pos Structure holding position of the value of the data element.
+    /// It comprises the line number and the position within this line. The values
+    /// held in this structure are used for error logging purposes.
+    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 +147,14 @@ public:
     virtual ~Element() {};
 
     /// \return the type of this element
-    int getType() const { return (type); }
+    int getType() const { return (type_); }
+
+    /// \brief Returns position where the data element's value starts in a
+    /// configuration string.
+    ///
+    /// @warning The returned reference is valid as long as the object which
+    /// created it lives.
+    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 +347,35 @@ 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());
+    static ElementPtr create(const long int i,
+                             const Position& pos = ZERO_POSITION());
+    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());
 
     /// \brief Creates an empty ListElement type ElementPtr.
-    static ElementPtr createList();
+    ///
+    /// \param pos A structure holding position of the data element value
+    /// in the configuration string. It is used for error logging purposes.
+    static ElementPtr createList(const Position& pos = ZERO_POSITION());
 
     /// \brief Creates an empty MapElement type ElementPtr.
-    static ElementPtr createMap();
+    ///
+    /// \param pos A structure holding position of the data element value
+    /// in the configuration string. It is used for error logging purposes.
+    static ElementPtr createMap(const Position& pos = ZERO_POSITION());
     //@}
 
 
@@ -321,7 +399,8 @@ public:
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     static ElementPtr fromJSON(std::istream& in) throw(JSONError);
-    static ElementPtr fromJSON(std::istream& in, const std::string& file_name) throw(JSONError);
+    static ElementPtr fromJSON(std::istream& in, const std::string& file_name)
+        throw(JSONError);
 
     /// Creates an Element from the given input stream, where we keep
     /// track of the location in the stream for error reporting.
@@ -335,7 +414,9 @@ public:
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     // make this one private?
-    static ElementPtr fromJSON(std::istream& in, const std::string& file, int& line, int &pos) throw(JSONError);
+    static ElementPtr fromJSON(std::istream& in, const std::string& file,
+                               int& line, int &pos)
+        throw(JSONError);
     //@}
 
     /// \name Type name conversion functions
@@ -386,7 +467,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 definitions
 ///           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 +477,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 +492,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 +507,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 +520,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 +530,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 +545,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 +577,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);
     }

+ 84 - 1
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_);
+
+}
+
 }