Browse Source

Merge ticket 172 (JSON on cc channel)

This merge expands isc::data::Element with full JSON support, and removes the binary wire format. Python side uses built-in JSON module.


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2364 e5f2f494-b856-4b98-b285-d166d9295462
Jelte Jansen 15 years ago
parent
commit
31b31ad883

+ 1 - 1
src/bin/auth/auth_srv.cc

@@ -274,7 +274,7 @@ AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
         bool is_default;
         string item("database_file");
         ElementPtr value = cs_->getValue(is_default, item);
-        final = Element::createFromString("{}");
+        final = Element::createMap();
 
         // If the value is the default, and we are running from
         // a specific directory ('from build'), we need to use

+ 1 - 1
src/bin/auth/tests/auth_srv_unittest.cc

@@ -236,7 +236,7 @@ updateConfig(AuthSrv* server, const char* const dbfile,
              const bool expect_success)
 {
     const ElementPtr config_answer =
-        server->updateConfig(Element::createFromString(dbfile));
+        server->updateConfig(Element::fromJSON(dbfile));
     EXPECT_EQ(Element::map, config_answer->getType());
     EXPECT_TRUE(config_answer->contains("result"));
 

+ 227 - 439
src/lib/cc/data.cc

@@ -26,29 +26,34 @@
 
 #include <boost/algorithm/string.hpp> // for iequals
 
+#include <cmath>
+
 using namespace std;
 
-namespace {
-const unsigned char PROTOCOL_VERSION[4] = { 0x53, 0x6b, 0x61, 0x6e };
+namespace isc {
+namespace data {
 
-const unsigned char ITEM_BLOB = 0x01;
-const unsigned char ITEM_HASH = 0x02;
-const unsigned char ITEM_LIST = 0x03;
-const unsigned char ITEM_NULL = 0x04;
-const unsigned char ITEM_BOOL = 0x05;
-const unsigned char ITEM_INT  = 0x06;
-const unsigned char ITEM_REAL = 0x07;
-const unsigned char ITEM_UTF8 = 0x08;
-const unsigned char ITEM_MASK = 0x0f;
+std::string
+Element::str()
+{
+    std::stringstream ss;
+    toJSON(ss);
+    return ss.str();
+}
 
-const unsigned char ITEM_LENGTH_32   = 0x00;
-const unsigned char ITEM_LENGTH_16   = 0x10;
-const unsigned char ITEM_LENGTH_8    = 0x20;
-const unsigned char ITEM_LENGTH_MASK = 0x30;
+std::string
+Element::toWire()
+{
+    std::stringstream ss;
+    toJSON(ss);
+    return ss.str();
 }
 
-namespace isc {
-namespace data {
+void
+Element::toWire(std::ostream& ss)
+{
+    toJSON(ss);
+}
 
 //
 // The following methods are effectively empty, and their parameters are
@@ -58,7 +63,7 @@ namespace data {
 // installed files we define the methods here.
 //
 bool
-Element::getValue(int& t UNUSED_PARAM) {
+Element::getValue(long int& t UNUSED_PARAM) {
     return false;
 }
 
@@ -88,7 +93,7 @@ Element::getValue(std::map<std::string, ElementPtr>& t UNUSED_PARAM) {
 }
 
 bool
-Element::setValue(const int v UNUSED_PARAM) {
+Element::setValue(const long int v UNUSED_PARAM) {
     return false;
 }
 
@@ -179,15 +184,11 @@ Element::find(const std::string& identifier UNUSED_PARAM,
 
 namespace {
 inline void
-throwParseError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
+throwJSONError(const std::string& error, const std::string& file, int line, int pos)
 {
-    if (line != 0 || pos != 0) {
-        std::stringstream ss;
-        ss << error << " in " + file + ":" << line << ":" << pos;
-        throw ParseError(ss.str());
-    } else {
-        throw ParseError(error);
-    }
+    std::stringstream ss;
+    ss << error << " in " + file + ":" << line << ":" << pos;
+    isc_throw(JSONError, ss.str());
 }
 }
 
@@ -203,7 +204,12 @@ bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b) {
 // factory functions
 //
 ElementPtr
-Element::create(const int i) {
+Element::create() {
+    return ElementPtr(new NullElement());
+}
+
+ElementPtr
+Element::create(const long int i) {
     return ElementPtr(new IntElement(i));
 }
 
@@ -223,24 +229,18 @@ Element::create(const bool b) {
 }
 
 ElementPtr
-Element::create(const std::vector<ElementPtr>& v) {
-    return ElementPtr(new ListElement(v));
+Element::createList() {
+    return ElementPtr(new ListElement());
 }
 
 ElementPtr
-Element::create(const std::map<std::string, ElementPtr>& m) {
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
-         it != m.end(); ++it) {
-        if ((*it).first.length() > 255) {
-            isc_throw(TypeError, "Map tag is too long");
-        }
-    }
-    return ElementPtr(new MapElement(m));
+Element::createMap() {
+    return ElementPtr(new MapElement());
 }
 
 
 //
-// helper functions for createFromString factory
+// helper functions for fromJSON factory
 //
 namespace {
 bool
@@ -300,15 +300,17 @@ skip_to(std::istream &in, const std::string& file, int& line,
             --pos;
             return;
         } else {
-            throwParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
+            throwJSONError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
         }
     }
-    throwParseError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
+    throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
 }
 
+// TODO: Should we check for all other official escapes here (and
+// error on the rest)?
 std::string
 str_from_stringstream(std::istream &in, const std::string& file, const int line,
-                      int& pos) throw (ParseError)
+                      int& pos) throw (JSONError)
 {
     char c = 0;
     std::stringstream ss;
@@ -318,7 +320,7 @@ str_from_stringstream(std::istream &in, const std::string& file, const int line,
         c = in.get();
         ++pos;
     } else {
-        throwParseError("String expected", file, line, pos);
+        throwJSONError("String expected", file, line, pos);
     }
     while (c != EOF && c != '"') {
         ss << c;
@@ -342,36 +344,47 @@ word_from_stringstream(std::istream &in, int& pos) {
     return ss.str();
 }
 
-inline int
-count_chars_i(int i) {
-    int result = 1;
-    while (i > 10) {
-        ++result;
-        i = i / 10;
-    }
-    return result;
-}
-
-inline int
-count_chars_d(double d) {
-    int result = 1;
-    while (d < 1.0) {
-        ++result;
-        d = d * 10;
+static std::string
+number_from_stringstream(std::istream &in, int& pos) {
+    std::stringstream ss;
+    while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
+           in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
+        ss << (char) in.get();
     }
-    return result;
+    pos += ss.str().size();
+    return ss.str();
 }
 
+// Should we change from IntElement and DoubleElement to NumberElement
+// that can also hold an e value? (and have specific getters if the
+// value is larger than an int can handle)
 ElementPtr
-from_stringstream_int_or_double(std::istream &in, int &pos) {
-    int i;
-    in >> i;
-    pos += count_chars_i(i);
-    if (in.peek() == '.') {
-        double d;
-        in >> d;
-        pos += count_chars_d(i);
-        d += i;
+from_stringstream_number(std::istream &in, int &pos) {
+    long int i = 0;
+    double d = 0.0;
+    bool is_double = false;
+    char *endptr;
+
+    std::string number = number_from_stringstream(in, pos);
+
+    i = strtol(number.c_str(), &endptr, 10);
+    if (*endptr != '\0') {
+        d = strtod(number.c_str(), &endptr);
+        is_double = true;
+        if (*endptr != '\0') {
+            isc_throw(JSONError, std::string("Bad number: ") + number);
+        } else {
+            if (d == HUGE_VAL || d == -HUGE_VAL) {
+                isc_throw(JSONError, std::string("Number overflow: ") + number);
+            }
+        }
+    } else {
+        if (i == LONG_MAX || i == LONG_MIN) {
+            isc_throw(JSONError, std::string("Number overflow: ") + number);
+        }
+    }
+    
+    if (is_double) {
         return Element::create(d);
     } else {
         return Element::create(i);
@@ -388,8 +401,21 @@ from_stringstream_bool(std::istream &in, const std::string& file,
     } else if (boost::iequals(word, "False")) {
         return Element::create(false);
     } else {
-        throwParseError(std::string("Bad boolean value: ") + word, file, line, pos);
-        // above is a throw shortcur, return empty is never reached
+        throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
+        // above is a throw shortcurt, return empty is never reached
+        return ElementPtr();
+    }
+}
+
+ElementPtr
+from_stringstream_null(std::istream &in, const std::string& file,
+                       const int line, int& pos)
+{
+    const std::string word = word_from_stringstream(in, pos);
+    if (boost::iequals(word, "null")) {
+        return Element::create();
+    } else {
+        throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
         return ElementPtr();
     }
 }
@@ -404,27 +430,27 @@ ElementPtr
 from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
 {
     char c = 0;
-    std::vector<ElementPtr> v;
+    ElementPtr list = Element::createList();
     ElementPtr cur_list_element;
 
     skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != ']') {
         if (in.peek() != ']') {
-            cur_list_element = Element::createFromString(in, file, line, pos);
-            v.push_back(cur_list_element);
+            cur_list_element = Element::fromJSON(in, file, line, pos);
+            list->add(cur_list_element);
             skip_to(in, file, line, pos, ",]", " \t\n");
         }
         c = in.get();
         pos++;
     }
-    return Element::create(v);
+    return list;
 }
 
 ElementPtr
 from_stringstream_map(std::istream &in, const std::string& file, int& line,
                       int& pos)
 {
-    std::map<std::string, ElementPtr> m;
+    ElementPtr map = Element::createMap();
     skip_chars(in, " \t\n", line, pos);
     char c = in.peek();
     if (c == '}') {
@@ -432,45 +458,88 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
         c = in.get();
     } else {
         while (c != EOF && c != '}') {
-            std::pair<std::string, ElementPtr> p;
-
-            p.first = str_from_stringstream(in, file, line, pos);
-            if (p.first.length() > 255) {
-                // Map tag has one-byte length field in wire format, so the
-                // length cannot exceed 255.
-                throwParseError("Map tag is too long", file, line, pos);
-            }
+            std::string key = str_from_stringstream(in, file, line, pos);
 
             skip_to(in, file, line, pos, ":", " \t\n");
             // skip the :
             in.get();
             pos++;
-            p.second = Element::createFromString(in, file, line, pos);
-            m.insert(p);
+
+            ElementPtr value = Element::fromJSON(in, file, line, pos);
+            map->set(key, value);
+            
             skip_to(in, file, line, pos, ",}", " \t\n");
             c = in.get();
             pos++;
         }
     }
-    return Element::create(m);
+    return map;
 }
 }
 
+std::string
+Element::typeToName(Element::types type)
+{
+    switch(type) {
+    case Element::integer:
+        return std::string("integer");
+    case Element::real:
+        return std::string("real");
+    case Element::boolean:
+        return std::string("boolean");
+    case Element::string:
+        return std::string("string");
+    case Element::list:
+        return std::string("list");
+    case Element::map:
+        return std::string("map");
+    case Element::null:
+        return std::string("null");
+    case Element::any:
+        return std::string("any");
+    default:
+        return std::string("unknown");
+    }
+}
+
+Element::types
+Element::nameToType(const std::string& type_name) {
+    if (type_name == "integer") {
+        return Element::integer;
+    } else if (type_name == "real") {
+        return Element::real;
+    } else if (type_name == "boolean") {
+        return Element::boolean;
+    } else if (type_name == "string") {
+        return Element::string;
+    } else if (type_name == "list") {
+        return Element::list;
+    } else if (type_name == "map") {
+        return Element::map;
+    } else if (type_name == "null") {
+        return Element::null;
+    } else if (type_name == "any") {
+        return Element::any;
+    } else {
+        isc_throw(TypeError, type_name + " is not a valid type name");
+    }
+}
+
 ElementPtr
-Element::createFromString(std::istream& in) throw(ParseError) {
+Element::fromJSON(std::istream& in) throw(JSONError) {
     int line = 1, pos = 1;
-    return createFromString(in, "<istream>", line, pos);
+    return fromJSON(in, "<istream>", line, pos);
 }
 
 ElementPtr
-Element::createFromString(std::istream& in, const std::string& file_name) throw(ParseError)
+Element::fromJSON(std::istream& in, const std::string& file_name) throw(JSONError)
 {
     int line = 1, pos = 1;
-    return createFromString(in, file_name, line, pos);
+    return fromJSON(in, file_name, line, pos);
 }
 
 ElementPtr
-Element::createFromString(std::istream &in, const std::string& file, int& line, int& pos) throw(ParseError)
+Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos) throw(JSONError)
 {
     char c = 0;
     ElementPtr element;
@@ -490,8 +559,11 @@ Element::createFromString(std::istream &in, const std::string& file, int& line,
             case '8':
             case '9':
             case '0':
+            case '-':
+            case '+':
+            case '.':
                 in.putback(c);
-                element = from_stringstream_int_or_double(in, pos);
+                element = from_stringstream_number(in, pos);
                 el_read = true;
                 break;
             case 't':
@@ -502,6 +574,12 @@ Element::createFromString(std::istream &in, const std::string& file, int& line,
                 element = from_stringstream_bool(in, file, line, pos);
                 el_read = true;
                 break;
+            case 'n':
+            case 'N':
+                in.putback(c);
+                element = from_stringstream_null(in, file, line, pos);
+                el_read = true;
+                break;
             case '"':
                 in.putback('"');
                 element = from_stringstream_string(in, file, line, pos);
@@ -518,62 +596,65 @@ Element::createFromString(std::istream &in, const std::string& file, int& line,
             case EOF:
                 break;
             default:
-                throwParseError(std::string("error: unexpected character ") + c, file, line, pos);
+                throwJSONError(std::string("error: unexpected character ") + c, file, line, pos);
                 break;
         }
     }
     if (el_read) {
         return element;
     } else {
-        throw ParseError("nothing read");
+        isc_throw(JSONError, "nothing read");
     }
 }
 
 ElementPtr
-Element::createFromString(const std::string &in) {
+Element::fromJSON(const std::string &in) {
     std::stringstream ss;
     ss << in;
-    return createFromString(ss, "<string>");
+    return fromJSON(ss, "<string>");
 }
 
-//
-// a general to_str() function
-//
-std::string
-IntElement::str() {
-    std::stringstream ss;
+// to JSON format
+
+void
+IntElement::toJSON(std::ostream& ss)
+{
     ss << intValue();
-    return ss.str();
 }
 
-std::string
-DoubleElement::str() {
-    std::stringstream ss;
+void
+DoubleElement::toJSON(std::ostream& ss)
+{
     ss << doubleValue();
-    return ss.str();
 }
 
-std::string
-BoolElement::str() {
-    if (b) {
-        return "True";
+void
+BoolElement::toJSON(std::ostream& ss)
+{
+    if (boolValue()) {
+        ss << "true";
     } else {
-        return "False";
+        ss << "false";
     }
 }
 
-std::string
-StringElement::str() {
-    std::stringstream ss;
+void
+NullElement::toJSON(std::ostream& ss)
+{
+    ss << "null";
+}
+
+void
+StringElement::toJSON(std::ostream& ss)
+{
     ss << "\"";
     ss << stringValue();
     ss << "\"";
-    return ss.str();
 }
 
-std::string
-ListElement::str() {
-    std::stringstream ss;
+void
+ListElement::toJSON(std::ostream& ss)
+{
     ss << "[ ";
 
     const std::vector<ElementPtr>& v = listValue();
@@ -582,16 +663,15 @@ ListElement::str() {
         if (it != v.begin()) {
             ss << ", ";
         }
-        ss << (*it)->str();
+        (*it)->toJSON(ss);
     }
     ss << " ]";
-    return ss.str();
 }
 
-std::string
-MapElement::str() {
-    std::stringstream ss;
-    ss << "{";
+void
+MapElement::toJSON(std::ostream& ss)
+{
+    ss << "{ ";
 
     const std::map<std::string, ElementPtr>& m = mapValue();
     for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
@@ -601,13 +681,12 @@ MapElement::str() {
         }
         ss << "\"" << (*it).first << "\": ";
         if ((*it).second) {
-            ss << (*it).second->str();
+            (*it).second->toJSON(ss);
         } else {
             ss << "None";
         }
     }
-    ss << "}";
-    return ss.str();
+    ss << " }";
 }
 
 // throws when one of the types in the path (except the one
@@ -634,169 +713,12 @@ MapElement::find(const std::string& id) {
     }
 }
 
-//
-// Decode from wire format.
-//
-namespace {
-ElementPtr decode_element(std::stringstream& in, int& in_length);
-
-unsigned char
-get_byte(std::stringstream& in) {
-    const int c = in.get();
-    if (c == EOF) {
-        throw DecodeError("End of data while decoding wire format message");
-    }
-
-    return c;
-}
-
-std::string
-decode_tag(std::stringstream& in, int& item_length) {
-    char buf[256];
-
-    const int len = get_byte(in);
-    item_length--;
-
-    in.read(buf, len);
-    if (in.fail()) {
-        throw DecodeError();
-    }
-    buf[len] = 0;
-    item_length -= len;
-
-    return std::string(buf, len);
-}
-
-ElementPtr
-decode_bool(std::stringstream& in) {
-    const char c = in.get();
-    
-    if (c == '1') {
-        return Element::create(true);
-    } else {
-        return Element::create(false);
-    }
-}
-
-ElementPtr
-decode_int(std::stringstream& in) {
-    int me;
-    return from_stringstream_int_or_double(in, me);
-}
-
-ElementPtr
-decode_real(std::stringstream& in) {
-    int me;
-    return from_stringstream_int_or_double(in, me);
-}
-
-ElementPtr
-decode_blob(std::stringstream& in, const int item_length) {
-    vector<char> buf(item_length + 1);
-
-    in.read(&buf[0], item_length);
-    if (in.fail()) {
-        throw DecodeError();
-    }
-    buf[item_length] = 0;
-
-    return Element::create(std::string(&buf[0], item_length));
-}
-
-ElementPtr
-decode_hash(std::stringstream& in, int item_length) {
-    std::map<std::string, ElementPtr> m;
-    std::pair<std::string, ElementPtr> p;
-
-    while (item_length > 0) {
-        p.first = decode_tag(in, item_length);
-        p.second = decode_element(in, item_length);
-        m.insert(p);
-    }
-
-    return Element::create(m);
-}
-
-ElementPtr
-decode_list(std::stringstream& in, int item_length) {
-    std::vector<ElementPtr> v;
-
-    while (item_length > 0) {
-        v.push_back(decode_element(in, item_length));
-    }
-    return Element::create(v);
-}
-
-ElementPtr
-decode_null() {
-    return Element::create("NULL");
-}
-
-ElementPtr
-decode_element(std::stringstream& in, int& in_length) {
-    ElementPtr element;
-
-    const unsigned char type_and_length = get_byte(in);
-    const unsigned char type = type_and_length & ITEM_MASK;
-    const unsigned char lenbytes = type_and_length & ITEM_LENGTH_MASK;
-    in_length--;
-
-    int item_length = 0;
-    switch (lenbytes) {
-    case ITEM_LENGTH_32:
-        item_length |= get_byte(in);
-        item_length <<= 8;
-        item_length |= get_byte(in);
-        item_length <<= 8;
-        in_length -= 2;  // only 2 here, we will get more later
-    case ITEM_LENGTH_16:
-        item_length |= get_byte(in);
-        item_length <<= 8;
-        in_length--;  // only 1 here
-    case ITEM_LENGTH_8:
-        item_length |= get_byte(in);
-        in_length--;
-    }
-
-    in_length -= item_length;
-
-    switch (type) {
-    case ITEM_BOOL:
-        element = decode_bool(in);
-        break;
-    case ITEM_INT:
-        element = decode_int(in);
-        break;
-    case ITEM_REAL:
-        element = decode_real(in);
-        break;
-    case ITEM_BLOB:
-        element = decode_blob(in, item_length);
-        break;
-    case ITEM_UTF8:
-        // XXXMLG currently identical to decode_blob
-        element = decode_blob(in, item_length);
-        break;
-    case ITEM_HASH:
-        element = decode_hash(in, item_length);
-        break;
-    case ITEM_LIST:
-        element = decode_list(in, item_length);
-        break;
-    case ITEM_NULL:
-        element = decode_null();
-        break;
-    }
-
-    return (element);
-}
-}
-
 ElementPtr
 Element::fromWire(const std::string& s) {
     std::stringstream ss;
     ss << s;
-    return fromWire(ss, s.length());
+    int line = 0, pos = 0;
+    return fromJSON(ss, "<wire>", line, pos);
 }
 
 ElementPtr
@@ -804,159 +726,20 @@ Element::fromWire(std::stringstream& in, int length) {
     //
     // Check protocol version
     //
-    for (int i = 0 ; i < 4 ; ++i) {
-        const unsigned char version_byte = get_byte(in);
-        if (PROTOCOL_VERSION[i] != version_byte) {
-            throw DecodeError("Protocol version incorrect");
-        }
-    }
-    length -= 4;
-
-    return (decode_hash(in, length));
-}
-
-//
-// Encode into wire format.
-//
-
-std::string
-encode_length(const unsigned int length, unsigned char type) {
-    std::stringstream ss;
-
-    if (length <= 0x000000ff) {
-        const unsigned char val = (length & 0x000000ff);
-        type |= ITEM_LENGTH_8;
-        ss << type << val;
-    } else if (length <= 0x0000ffff) {
-        unsigned char val[2];
-        val[0] = (length & 0x0000ff00) >> 8;
-        val[1] = (length & 0x000000ff);
-        type |= ITEM_LENGTH_16;
-        ss << type << val[0] << val[1];
-    } else {
-        unsigned char val[4];
-        val[0] = (length & 0xff000000) >> 24;
-        val[1] = (length & 0x00ff0000) >> 16;
-        val[2] = (length & 0x0000ff00) >> 8;
-        val[3] = (length & 0x000000ff);
-        type |= ITEM_LENGTH_32;
-        ss << type << val[0] << val[1] << val[2] << val[3];
-    }
-    return ss.str();
-}
-
-std::string
-Element::toWire(const int omit_length) {
-    std::stringstream ss;
-    toWire(ss, omit_length);
-    return ss.str();
+    //for (int i = 0 ; i < 4 ; ++i) {
+    //    const unsigned char version_byte = get_byte(in);
+    //    if (PROTOCOL_VERSION[i] != version_byte) {
+    //        throw DecodeError("Protocol version incorrect");
+    //    }
+    //}
+    //length -= 4;
+    int line = 0, pos = 0;
+    return fromJSON(in, "<wire>", line, pos);
 }
 
 void
-StringElement::toWire(std::stringstream& ss,
-                      const int omit_length UNUSED_PARAM)
-{
-    unsigned int length = stringValue().length();
-    ss << encode_length(length, ITEM_UTF8) << stringValue();
-}
-
-void
-IntElement::toWire(std::stringstream& ss,
-                   const int omit_length UNUSED_PARAM)
-{
-    const std::string& s = str();
-    ss << encode_length(s.length(), ITEM_INT) << s;
-}
-
-void
-BoolElement::toWire(std::stringstream& ss,
-                    const int omit_length UNUSED_PARAM)
-{
-    ss << encode_length(1, ITEM_BOOL);
-    if (boolValue()) {
-        ss << '1';
-    } else {
-        ss << '0';
-    }
-}
-
-void
-DoubleElement::toWire(std::stringstream& ss,
-                      const int omit_length UNUSED_PARAM)
-{
-    std::stringstream text;
-
-    text << str();
-    const int length = text.str().length();
-    ss << encode_length(length, ITEM_REAL) << text.str();
-}
-
-void
-ListElement::toWire(std::stringstream& ss, const int omit_length) {
-    std::stringstream ss2;
-    const std::vector<ElementPtr>& v = listValue();
-    for (std::vector<ElementPtr>::const_iterator it = v.begin() ;
-         it != v.end() ; ++it) {
-        (*it)->toWire(ss2, 0);
-    }
-
-
-    if (omit_length) {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        if (ss2_buf->in_avail() > 0) {
-            ss << ss2_buf;
-        }
-    } else {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        ss << encode_length(ss2_buf->in_avail(), ITEM_LIST);
-        if (ss2_buf->in_avail() > 0) {
-            ss << ss2_buf;
-        }
-    }
-}
-
-void
-MapElement::toWire(std::stringstream& ss, int omit_length) {
-    std::stringstream ss2;
-
-    //
-    // If we don't want the length, we will want the protocol header
-    //
-    if (omit_length) {
-        ss2 << PROTOCOL_VERSION[0] << PROTOCOL_VERSION[1];
-        ss2 << PROTOCOL_VERSION[2] << PROTOCOL_VERSION[3];
-    }
-
-    const std::map<std::string, ElementPtr>& m = mapValue();
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
-         it != m.end(); ++it) {
-        const size_t taglen = (*it).first.length();
-        assert(taglen <= 0xff);
-        const unsigned char val = (taglen & 0x000000ff);
-        ss2 << val << (*it).first;
-
-        (*it).second->toWire(ss2, 0);
-    }
-
-    //
-    // add length if needed
-    //
-    if (omit_length) {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        if (ss2_buf->in_avail()) {
-            ss << ss2_buf;
-        }
-    } else {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        ss << encode_length(ss2_buf->in_avail(), ITEM_HASH);
-        if (ss2_buf->in_avail()) {
-            ss << ss2_buf;
-        }
-    }
+MapElement::set(const std::string& key, ElementPtr value) {
+    m[key] = value;
 }
 
 bool
@@ -992,6 +775,11 @@ BoolElement::equals(ElementPtr other) {
 }
 
 bool
+NullElement::equals(ElementPtr other) {
+    return other->getType() == Element::null;
+}
+
+bool
 StringElement::equals(ElementPtr other) {
     return (other->getType() == Element::string) &&
            (s == other->stringValue());
@@ -1082,7 +870,7 @@ merge(ElementPtr element, const ElementPtr other) {
     std::map<std::string, ElementPtr> m = other->mapValue();
     for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
-        if ((*it).second) {
+        if ((*it).second && (*it).second->getType() != Element::null) {
             element->set((*it).first, (*it).second);
         } else if (element->contains((*it).first)) {
             element->remove((*it).first);

+ 115 - 78
src/lib/cc/data.h

@@ -48,22 +48,10 @@ public:
 // i'd like to use Exception here but we need one that is derived from
 // runtime_error (as this one is directly based on external data, and
 // i want to add some values to any static data string that is provided)
-class ParseError : public std::runtime_error {
+class JSONError : public isc::Exception {
 public:
-    ParseError(const std::string &err) : std::runtime_error(err) {};
-};
-
-///
-/// \brief A standard Data module exception that is thrown if an error
-/// is found when decoding an Element from wire format
-///
-class DecodeError : public std::exception {
-public:
-    DecodeError(std::string m = "Wire-format data is invalid") : msg(m) {}
-    ~DecodeError() throw() {}
-    const char* what() const throw() { return msg.c_str(); }
-private:
-    std::string msg;
+    JSONError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
 };
 
 ///
@@ -75,7 +63,7 @@ private:
 ///
 /// Elements should in calling functions usually be referenced through
 /// an \c ElementPtr, which can be created using the factory functions
-/// \c Element::create() and \c Element::createFromString()
+/// \c Element::create() and \c Element::fromJSON()
 ///
 /// Notes to developers: Element is a base class, implemented by a
 /// specific subclass for each type (IntElement, BoolElement, etc).
@@ -97,48 +85,48 @@ protected:
 public:
     // any is a special type used in list specifications, specifying
     // that the elements can be of any type
-    enum types { integer, real, boolean, string, list, map, any };
+    enum types { integer, real, boolean, null, string, list, map, any };
     // base class; make dtor virtual
     virtual ~Element() {};
 
     /// \return the type of this element
     int getType() { return type; };
 
-    /// \returns true if the other ElementPtr has the same type and
-    ///          value
-    virtual bool equals(ElementPtr other) = 0;
-    
-    // pure virtuals, every derived class must implement these
-
     /// Returns a string representing the Element and all its
     /// child elements; note that this is different from stringValue(),
     /// which only returns the single value of a StringElement
-    /// A MapElement will be represented as { "name1": \<value1\>, "name2", \<value2\>, etc }
-    /// A ListElement will be represented as [ \<item1\>, \<item2\>, etc ]
-    /// All other elements will be represented directly
+    ///
+    /// The resulting string will contain the Element in JSON format.
     ///
     /// \return std::string containing the string representation
-    virtual std::string str() = 0;
+    std::string str();
 
     /// Returns the wireformat for the Element and all its child
     /// elements.
     ///
-    /// \param omit_length If this is non-zero, the item length will
-    ///        be omitted from the wire format
     /// \return std::string containing the element in wire format
-    std::string toWire(int omit_length = 1);
-    virtual void toWire(std::stringstream& out, int omit_length = 1) = 0;
+    std::string toWire();
+    void toWire(std::ostream& out);
+
+    /// \name pure virtuals, every derived class must implement these
+
+    /// \returns true if the other ElementPtr has the same type and
+    ///          value
+    virtual bool equals(ElementPtr other) = 0;
+    
+    /// Converts the Element to JSON format and appends it to
+    /// the given stringstream.
+    virtual void toJSON(std::ostream& ss) = 0;
 
     /// \name Type-specific getters
     ///
-    ///
     /// \brief These functions only
     /// work on their corresponding Element type. For all other
     /// types, a TypeError is thrown.
     /// If you want an exception-safe getter method, use
     /// getValue() below
     //@{
-    virtual int intValue() { isc_throw(TypeError, "intValue() called on non-integer Element"); };
+    virtual long int intValue() { isc_throw(TypeError, "intValue() called on non-integer Element"); };
     virtual double doubleValue() { isc_throw(TypeError, "doubleValue() called on non-double Element"); };
     virtual bool boolValue() { isc_throw(TypeError, "boolValue() called on non-Bool Element"); };
     virtual std::string stringValue() { isc_throw(TypeError, "stringValue() called on non-string Element"); };
@@ -155,13 +143,14 @@ public:
     /// data to the given reference and returning true
     ///
     //@{
-    virtual bool getValue(int& t);
+    virtual bool getValue(long int& t);
     virtual bool getValue(double& t);
     virtual bool getValue(bool& t);
     virtual bool getValue(std::string& t);
     virtual bool getValue(std::vector<ElementPtr>& t);
     virtual bool getValue(std::map<std::string, ElementPtr>& t);
     //@}
+
     ///
     /// \name Exception-safe setters.
     ///
@@ -170,7 +159,7 @@ public:
     /// is of the correct type
     ///
     //@{
-    virtual bool setValue(const int v);
+    virtual bool setValue(const long int v);
     virtual bool setValue(const double v);
     virtual bool setValue(const bool t);
     virtual bool setValue(const std::string& v);
@@ -191,21 +180,26 @@ public:
     /// of bounds, this function throws an std::out_of_range exception.
     /// \param i The position of the ElementPtr to return
     virtual ElementPtr get(const int i);
+
     /// Sets the ElementPtr at the given index. If the index is out
     /// of bounds, this function throws an std::out_of_range exception.
     /// \param i The position of the ElementPtr to set
     /// \param element The ElementPtr to set at the position
     virtual void set(const size_t i, ElementPtr element);
+
     /// Adds an ElementPtr to the list
     /// \param element The ElementPtr to add
     virtual void add(ElementPtr element);
+
     /// Removes the element at the given position. If the index is out
     /// of nothing happens.
     /// \param i The index of the element to remove.
     virtual void remove(const int i);
+
     /// Returns the number of elements in the list.
     virtual size_t size();
     //@}
+
     
     /// \name MapElement functions
     ///
@@ -216,16 +210,20 @@ public:
     /// \param name The key of the Element to return
     /// \return The ElementPtr at the given key
     virtual ElementPtr get(const std::string& name);
+
     /// Sets the ElementPtr at the given key
     /// \param name The key of the Element to set
     virtual void set(const std::string& name, ElementPtr element);
+
     /// Remove the ElementPtr at the given key
     /// \param name The key of the Element to remove
     virtual void remove(const std::string& name);
+
     /// Checks if there is data at the given key
     /// \param name The key of the Element to remove
     /// \return true if there is data at the key, false if not.
     virtual bool contains(const std::string& name);
+
     /// Recursively finds any data at the given identifier. The
     /// identifier is a /-separated list of names of nested maps, with
     /// the last name being the leaf that is returned.
@@ -240,6 +238,7 @@ public:
     /// null ElementPtr if it is not found, which can be checked with
     /// Element::is_null(ElementPtr e).
     virtual ElementPtr find(const std::string& identifier);
+
     /// See \c Element::find()
     /// \param identifier The identifier of the element to find
     /// \param t Reference to store the resulting ElementPtr, if found.
@@ -247,6 +246,7 @@ public:
     virtual bool find(const std::string& identifier, ElementPtr& t);
     //@}
 
+
     /// \name Factory functions
     
     // TODO: should we move all factory functions to a different class
@@ -257,39 +257,52 @@ public:
     /// \brief These functions simply wrap the given data directly
     /// in an Element object, and return a reference to it, in the form
     /// of an \c ElementPtr.
-    /// If there is a memory allocation problem, these functions will
-    /// return a NULL ElementPtr, which can be checked with
-    /// Element::is_null(ElementPtr ep).
+    /// These factory functions are exception-free (unless there is
+    /// no memory available, in which case bad_alloc is raised by the
+    /// underlying system).
+    /// (Note that that is different from an NullElement, which
+    /// represents an empty value, and is created with Element::create())
     //@{
-    static ElementPtr create(const int i);
+    static ElementPtr create();
+    static ElementPtr create(const long int i);
+    static ElementPtr create(const int i) { return create(static_cast<long int>(i)); };
     static ElementPtr create(const double d);
     static ElementPtr create(const bool b);
     static ElementPtr create(const std::string& s);
     // 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 std::vector<ElementPtr>& v);
-    static ElementPtr create(const std::map<std::string, ElementPtr>& m);
+    static ElementPtr create(const char *s) { return create(std::string(s)); };
+
+    /// \brief Creates an empty ListElement type ElementPtr.
+    static ElementPtr createList();
+
+    /// \brief Creates an empty MapElement type ElementPtr.
+    static ElementPtr createMap();
     //@}
 
+
     /// \name Compound factory functions
 
-    /// \brief These functions will parse the given string representation
-    /// of a compound element. If there is a parse error, an exception
-    /// of the type isc::data::ParseError is thrown.
+    /// \brief These functions will parse the given string (JSON)
+    /// representation  of a compound element. If there is a parse
+    /// error, an exception of the type isc::data::JSONError is thrown.
 
     //@{
-    /// Creates an Element from the given string
+    /// Creates an Element from the given JSON string
     /// \param in The string to parse the element from
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given string.
-    static ElementPtr createFromString(const std::string& in);
-    /// Creates an Element from the given input stream
+    static ElementPtr fromJSON(const std::string& in);
+
+    /// Creates an Element from the given input stream containing JSON
+    /// formatted data.
+    ///
     /// \param in The string to parse the element from
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
-    static ElementPtr createFromString(std::istream& in) throw(ParseError);
-    static ElementPtr createFromString(std::istream& in, const std::string& file_name) throw(ParseError);
+    static ElementPtr fromJSON(std::istream& in) 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.
     ///
@@ -301,9 +314,25 @@ public:
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     // make this one private?
-    static ElementPtr createFromString(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
+    static ElementPtr fromJSON(std::istream& in, const std::string& file, int& line, int &pos) throw(JSONError);
     //@}
 
+    /// \name Type name conversion functions
+
+    /// Returns the name of the given type as a string
+    ///
+    /// \param type The type to return the name of
+    /// \return The name of the type, or "unknown" if the type
+    ///         is not known.
+    static std::string typeToName(Element::types type);
+
+    /// Converts the string to the corresponding type
+    /// Throws a TypeError if the name is unknown.
+    ///
+    /// \param type_name The name to get the type of
+    /// \return the corresponding type value
+    static Element::types nameToType(const std::string& type_name);
+
     /// \name Wire format factory functions
 
     /// These function pparse the wireformat at the given stringstream
@@ -313,11 +342,18 @@ public:
     //@{
     /// Creates an Element from the wire format in the given
     /// stringstream of the given length.
+    /// Since the wire format is JSON, thise is the same as
+    /// fromJSON, and could be removed.
+    ///
     /// \param in The input stringstream.
     /// \param length The length of the wireformat data in the stream
     /// \return ElementPtr with the data that is parsed.
     static ElementPtr fromWire(std::stringstream& in, int length);
+
     /// Creates an Element from the wire format in the given string
+    /// Since the wire format is JSON, thise is the same as
+    /// fromJSON, and could be removed.
+    ///
     /// \param s The input string
     /// \return ElementPtr with the data that is parsed.
     static ElementPtr fromWire(const std::string& s);
@@ -325,17 +361,16 @@ public:
 };
 
 class IntElement : public Element {
-    int i;
+    long int i;
 
 public:
-    IntElement(int v) : Element(integer), i(v) { };
-    int intValue() { return i; }
+    IntElement(long int v) : Element(integer), i(v) { };
+    long int intValue() { return i; }
     using Element::getValue;
-    bool getValue(int& t) { t = i; return true; };
+    bool getValue(long int& t) { t = i; return true; };
     using Element::setValue;
-    bool setValue(const int v) { i = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    bool setValue(const long int v) { i = v; return true; };
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -349,8 +384,7 @@ public:
     bool getValue(double& t) { t = d; return true; };
     using Element::setValue;
     bool setValue(const double v) { d = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -364,8 +398,14 @@ public:
     bool getValue(bool& t) { t = b; return true; };
     using Element::setValue;
     bool setValue(const bool v) { b = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
+    bool equals(ElementPtr other);
+};
+
+class NullElement : public Element {
+public:
+    NullElement() : Element(null) {};
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -379,8 +419,7 @@ public:
     bool getValue(std::string& t) { t = s; return true; };
     using Element::setValue;
     bool setValue(const std::string& v) { s = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -388,7 +427,7 @@ class ListElement : public Element {
     std::vector<ElementPtr> l;
 
 public:
-    ListElement(std::vector<ElementPtr> v) : Element(list), l(v) {};
+    ListElement() : Element(list), l(std::vector<ElementPtr>()) {};
     const std::vector<ElementPtr>& listValue() { return l; }
     using Element::getValue;
     bool getValue(std::vector<ElementPtr>& t) { t = l; return true; };
@@ -401,8 +440,7 @@ public:
     void add(ElementPtr e) { l.push_back(e); };
     using Element::remove;
     void remove(int i) { l.erase(l.begin() + i); };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     size_t size() { return l.size(); }
     bool equals(ElementPtr other);
 };
@@ -411,7 +449,8 @@ class MapElement : public Element {
     std::map<std::string, ElementPtr> m;
 
 public:
-    MapElement(const std::map<std::string, ElementPtr>& v) : Element(map), m(v) {};
+    MapElement() : Element(map), m(std::map<std::string, ElementPtr>()) {};
+    // TODO: should we have direct iterators instead of exposing the std::map here?
     const std::map<std::string, ElementPtr>& mapValue() { return m; }
     using Element::getValue;
     bool getValue(std::map<std::string, ElementPtr>& t) { t = m; return true; };
@@ -420,18 +459,12 @@ public:
     using Element::get;
     ElementPtr get(const std::string& s) { if (contains(s)) { return m[s]; } else { return ElementPtr();} };
     using Element::set;
-    void set(const std::string& s, ElementPtr p) { m[s] = p; };
+    void set(const std::string& key, ElementPtr value);
     using Element::remove;
     void remove(const std::string& s) { m.erase(s); }
     bool contains(const std::string& s) { return m.find(s) != m.end(); }
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     
-    //
-    // Encode into the CC wire format.
-    //
-    void toWire(std::ostream& ss);
-
     // we should name the two finds better...
     // find the element at id; raises TypeError if one of the
     // elements at path except the one we're looking for is not a
@@ -467,8 +500,12 @@ void removeIdentical(ElementPtr a, const ElementPtr b);
 /// MapElements.
 /// Every string,value pair in other is copied into element
 /// (the ElementPtr of value is copied, this is not a new object)
-/// Unless the value is an empty ElementPtr, in which case the
-/// whole key is removed from element.
+/// Unless the value is a NullElement, in which case the
+/// key is removed from element, rather than setting the value to
+/// the given NullElement.
+/// This way, we can remove values from for instance maps with
+/// configuration data (which would then result in reverting back
+/// to the default).
 /// Raises a TypeError if either ElementPtr is not a MapElement
 void merge(ElementPtr element, const ElementPtr other);
 

+ 370 - 223
src/lib/cc/data_unittests.cc

@@ -40,120 +40,275 @@ TEST(Element, type) {
     EXPECT_EQ(bool_el.getType(), Element::boolean);
     StringElement str_el = StringElement("foo");
     EXPECT_EQ(str_el.getType(), Element::string);
-    std::vector<ElementPtr> v;
-    ListElement list_el = ListElement(v);
+    ListElement list_el = ListElement();
     EXPECT_EQ(list_el.getType(), Element::list);
-    std::map<std::string, ElementPtr> m;
-    MapElement map_el = MapElement(m);
+    MapElement map_el = MapElement();
     EXPECT_EQ(map_el.getType(), Element::map);
+
 }
 
-TEST(Element, from_and_to_str) {
-    // this test checks whether the str() method returns the same
-    // string that was used for creation
+TEST(Element, TypeNameConversion) {
+    EXPECT_EQ(Element::integer, Element::nameToType("integer"));
+    EXPECT_EQ(Element::real, Element::nameToType("real"));
+    EXPECT_EQ(Element::boolean, Element::nameToType("boolean"));
+    EXPECT_EQ(Element::string, Element::nameToType("string"));
+    EXPECT_EQ(Element::list, Element::nameToType("list"));
+    EXPECT_EQ(Element::map, Element::nameToType("map"));
+    EXPECT_EQ(Element::null, Element::nameToType("null"));
+    EXPECT_EQ(Element::any, Element::nameToType("any"));
+    EXPECT_THROW(Element::nameToType("somethingunknown"), TypeError);
+
+    EXPECT_EQ("integer", Element::typeToName(Element::integer));
+    EXPECT_EQ("real", Element::typeToName(Element::real));
+    EXPECT_EQ("boolean", Element::typeToName(Element::boolean));
+    EXPECT_EQ("string", Element::typeToName(Element::string));
+    EXPECT_EQ("list", Element::typeToName(Element::list));
+    EXPECT_EQ("map", Element::typeToName(Element::map));
+    EXPECT_EQ("null", Element::typeToName(Element::null));
+    EXPECT_EQ("any", Element::typeToName(Element::any));
+    EXPECT_EQ("unknown", Element::typeToName((Element::types)123));
+}
+
+TEST(Element, from_and_to_json) {
+    // a set of inputs that are the same when converted to json and
+    // back to a string (tests for inputs that have equivalent, but
+    // different string representations when converted back are below)
     ElementPtr el;
     std::vector<std::string> sv;
 
     sv.push_back("12");
     sv.push_back("1.1");
-    sv.push_back("True");
-    sv.push_back("False");
+    sv.push_back("true");
+    sv.push_back("false");
     sv.push_back("\"asdf\"");
+    sv.push_back("null");
     sv.push_back("[ 1, 2, 3, 4 ]");
-    sv.push_back("{\"name\": \"foo\", \"value\": 47806}");
-    sv.push_back("[ {\"a\": 1, \"b\": \"c\"}, {\"a\": 2, \"b\": \"d\"} ]");
-
+    sv.push_back("{ \"name\": \"foo\", \"value\": 47806 }");
+    sv.push_back("[ { \"a\": 1, \"b\": \"c\" }, { \"a\": 2, \"b\": \"d\" } ]");
+    sv.push_back("8.23");
+    sv.push_back("123.456");
+    sv.push_back("null");
+    sv.push_back("-1");
+    sv.push_back("-1.234");
+    sv.push_back("-123.456");
+    
     BOOST_FOREACH(std::string s, sv) {
-        // also test << operator, which uses Element::str()
+        // test << operator, which uses Element::str()
         std::ostringstream stream;
-        el = Element::createFromString(s);
+        el = Element::fromJSON(s);
         stream << el;
         EXPECT_EQ(stream.str(), s);
+
+        // test toWire(ostream), which should also be the same now
+        std::ostringstream wire_stream;
+        el->toWire(wire_stream);
+        EXPECT_EQ(wire_stream.str(), s);
     }
 
     // some parse errors
     try {
-        Element::createFromString("{1}");
-    } catch (isc::data::ParseError pe) {
+        Element::fromJSON("{1}");
+    } catch (isc::data::JSONError pe) {
         std::string s = std::string(pe.what());
         EXPECT_EQ(s, "String expected in <string>:1:3");
     }
     
     sv.clear();
     sv.push_back("{1}");
-    //ElementPtr ep = Element::createFromString("\"aaa\nbbb\"err");
+    //ElementPtr ep = Element::fromJSON("\"aaa\nbbb\"err");
     //std::cout << ep << std::endl;
     sv.push_back("\n\nTru");
     sv.push_back("{ \n \"aaa\nbbb\"err:");
     sv.push_back("{ \t\n \"aaa\nbbb\"\t\n\n:\n True, \"\\\"");
     sv.push_back("{ \"a\": None}");
     sv.push_back("");
+    sv.push_back("nul");
     BOOST_FOREACH(std::string s, sv) {
         
-        EXPECT_THROW(el = Element::createFromString(s), isc::data::ParseError);
+        EXPECT_THROW(el = Element::fromJSON(s), isc::data::JSONError);
     }
 
+    // some json specific format tests, here the str() output is
+    // different from the string input
+    EXPECT_EQ("100", Element::fromJSON("+100")->str());
+    EXPECT_EQ("100", Element::fromJSON("1e2")->str());
+    EXPECT_EQ("100", Element::fromJSON("+1e2")->str());
+    EXPECT_EQ("-100", Element::fromJSON("-1e2")->str());
+    EXPECT_EQ("0.01", Element::fromJSON("1e-2")->str());
+    EXPECT_EQ("0.01", Element::fromJSON(".01")->str());
+    EXPECT_EQ("-0.01", Element::fromJSON("-1e-2")->str());
+    EXPECT_EQ("1.2", Element::fromJSON("1.2")->str());
+    EXPECT_EQ("1", Element::fromJSON("1.0")->str());
+    EXPECT_EQ("120", Element::fromJSON("1.2e2")->str());
+    EXPECT_EQ("100", Element::fromJSON("1.0e2")->str());
+    EXPECT_EQ("100", Element::fromJSON("1.0E2")->str());
+    EXPECT_EQ("0.01", Element::fromJSON("1.0e-2")->str());
+    EXPECT_EQ("0.012", Element::fromJSON("1.2e-2")->str());
+    EXPECT_EQ("0.012", Element::fromJSON("1.2E-2")->str());
+    EXPECT_EQ("null", Element::fromJSON("Null")->str());
+    EXPECT_EQ("null", Element::fromJSON("NULL")->str());
+    EXPECT_EQ("false", Element::fromJSON("False")->str());
+    EXPECT_EQ("false", Element::fromJSON("FALSE")->str());
+    EXPECT_EQ("true", Element::fromJSON("True")->str());
+    EXPECT_EQ("true", Element::fromJSON("TRUE")->str());
+
+    // number overflows
+    EXPECT_THROW(Element::fromJSON("12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1.1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("-1.1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1e50000")->str(), JSONError);
+
 }
 
 TEST(Element, create_and_value_throws) {
     // this test checks whether elements throw exceptions if the
     // incorrect type is requested
     ElementPtr el;
+    long int i;
+    double d;
+    bool b;
+    std::string s("asdf");
+    std::vector<ElementPtr> v;
+    std::map<std::string, ElementPtr> m;
+    
 
     el = Element::create(1);
+    EXPECT_NO_THROW(el->intValue());
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
+    EXPECT_TRUE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(i, 1);
+    i = 2;
+    EXPECT_TRUE(el->setValue(i));
+    EXPECT_EQ(2, el->intValue());
+    EXPECT_FALSE(el->setValue(d));
+    EXPECT_FALSE(el->setValue(b));
+    EXPECT_FALSE(el->setValue(s));
+    EXPECT_FALSE(el->setValue(v));
+    EXPECT_FALSE(el->setValue(m));
+    EXPECT_THROW(el->get(1), TypeError);
+    EXPECT_THROW(el->set(1, el), TypeError);
+    EXPECT_THROW(el->add(el), TypeError);
+    EXPECT_THROW(el->remove(1), TypeError);
+    EXPECT_THROW(el->size(), TypeError);
+    EXPECT_THROW(el->get("foo"), TypeError);
+    EXPECT_THROW(el->set("foo", el), TypeError);
+    EXPECT_THROW(el->remove("foo"), TypeError);
+    EXPECT_THROW(el->contains("foo"), TypeError);
+    ElementPtr tmp;
+    EXPECT_FALSE(el->find("foo", tmp));
+    
 
     el = Element::create(1.1);
     EXPECT_THROW(el->intValue(), TypeError);
+    EXPECT_NO_THROW(el->doubleValue());
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_TRUE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(d, 1.1);
+    d = 2.2;
+    EXPECT_TRUE(el->setValue(d));
+    EXPECT_EQ(2.2, el->doubleValue());
+    EXPECT_FALSE(el->setValue(i));
+    EXPECT_FALSE(el->setValue(b));
+    EXPECT_FALSE(el->setValue(s));
+    EXPECT_FALSE(el->setValue(v));
+    EXPECT_FALSE(el->setValue(m));
 
     el = Element::create(true);
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
+    EXPECT_NO_THROW(el->boolValue());
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_TRUE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(b, true);
+    b = false;
+    EXPECT_TRUE(el->setValue(b));
+    EXPECT_EQ(false, el->boolValue());
 
     el = Element::create("foo");
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
+    EXPECT_NO_THROW(el->stringValue());
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
-
-    std::vector<ElementPtr> v;
-    el = Element::create(v);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_TRUE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(s, "foo");
+    s = "bar";
+    EXPECT_TRUE(el->setValue(s));
+    EXPECT_EQ("bar", el->stringValue());
+
+    el = Element::createList();
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
+    EXPECT_NO_THROW(el->listValue());
     EXPECT_THROW(el->mapValue(), TypeError);
-
-    std::map<std::string, ElementPtr> m;
-    el = Element::create(m);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_TRUE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ("[  ]", el->str());
+    v.push_back(Element::create(1));
+    EXPECT_TRUE(el->setValue(v));
+    EXPECT_EQ("[ 1 ]", el->str());
+
+    el = Element::createMap();
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
+    EXPECT_NO_THROW(el->mapValue());
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_TRUE(el->getValue(m));
 
 }
 
 TEST(Element, ListElement) {
     // this function checks the specific functions for ListElements
-    ElementPtr el = Element::createFromString("[ 1, \"bar\", 3 ]");
+    ElementPtr el = Element::fromJSON("[ 1, \"bar\", 3 ]");
     EXPECT_EQ(el->get(0)->intValue(), 1);
     EXPECT_EQ(el->get(1)->stringValue(), "bar");
     EXPECT_EQ(el->get(2)->intValue(), 3);
 
-    el->set(0, Element::createFromString("\"foo\""));
+    el->set(0, Element::fromJSON("\"foo\""));
     EXPECT_EQ(el->get(0)->stringValue(), "foo");
 
     el->add(Element::create(47806));
@@ -170,18 +325,9 @@ TEST(Element, ListElement) {
     EXPECT_EQ(el->get(2)->intValue(), 32);
 }
 
-namespace {
-const string long_maptag("0123456789abcdef1123456789abcdef2123456789abcdef"
-                         "3123456789abcdef4123456789abcdef5123456789abcdef"
-                         "6123456789abcdef7123456789abcdef8123456789abcdef"
-                         "9123456789abcdefa123456789abcdefb123456789abcdef"
-                         "c123456789abcdefd123456789abcdefe123456789abcdef"
-                         "f123456789abcdef");
-}
-
 TEST(Element, MapElement) {
     // this function checks the specific functions for ListElements
-    ElementPtr el = Element::createFromString("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
+    ElementPtr el = Element::fromJSON("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
     ElementPtr el2;
     
     EXPECT_EQ(el->get("name")->stringValue(), "foo");
@@ -211,240 +357,241 @@ TEST(Element, MapElement) {
                        "9123456789abcdefa123456789abcdefb123456789abcdef"
                        "c123456789abcdefd123456789abcdefe123456789abcdef"
                        "f123456789abcde");
-    std::map<std::string, ElementPtr> long_maptag_map;
     
     EXPECT_EQ(255, long_maptag.length()); // check prerequisite
-    el = Element::createFromString("{ \"" + long_maptag + "\": \"bar\"}");
+    el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}");
     EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
 
-    long_maptag_map[long_maptag] = Element::create("bar");
-    el = Element::create(long_maptag_map);
+    el = Element::createMap();
+    el->set(long_maptag, Element::create("bar"));
     EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
 
-    // A one-byte longer tag should trigger an exception.
+    // A one-byte longer tag should still be allowed
     long_maptag.push_back('f');
-    EXPECT_THROW(Element::createFromString("{ \"" + long_maptag +
-                                           "\": \"bar\"}"),
-                 ParseError);
-
-    long_maptag_map[long_maptag] = Element::create("bar");
-    EXPECT_THROW(Element::create(long_maptag_map), TypeError);
+    el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}");
+    el->set(long_maptag, Element::create("bar"));
+    EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
 
 }
 
 TEST(Element, to_and_from_wire) {
-    ElementPtr el, decoded_el;
-    std::string wire;
-    std::vector<std::string> sv;
-    std::vector<std::string> sw;
-    std::stringstream bigstring, bigstring2;
-    std::stringstream bigwire, bigwire2;
-
-    sv.push_back("{\"name\": \"foo\"}");
-    sw.push_back("Skan\004name\050\003foo");
-    sv.push_back("{\"value2\": {\"number\": 42}}");
-    sw.push_back("Skan\006value2\042\013\006number\046\00242");
-    sv.push_back("{\"bool\": False, \"bool2\": True, \"real\": 2.34, \"string\": \"foo\"}");
-    sw.push_back("Skan\004bool\045\0010\005bool2\045\0011\004real\047\0042\05634\006string\050\003foo");
-    sv.push_back("{\"list\": [ 1, 2, 3, 4 ]}");
-    sw.push_back("Skan\004list\043\014\046\0011\046\0012\046\0013\046\0014");
-
-    // some big ones
-
-    bigstring << "{\"bigstring\": \"";
-    bigwire << "Skan\011bigstring\030\001\001";
-    for (size_t i = 0; i < 257; i++) {
-        bigstring << "x";
-        bigwire << "x";
-    }
-    bigstring << "\"}";
-    sv.push_back(bigstring.str());
-    sw.push_back(bigwire.str());
-
-
-    bigstring2 << "{\"bigstring2\": \"";
-    bigwire2 << "Skan\012bigstring2\010";
-    bigwire2 << '\000' << '\001' << '\000' << '\001';
-    for (size_t i = 0; i < 65537; i++) {
-        bigstring2 << "x";
-        bigwire2 << "x";
-    }
-    bigstring2 << "\"}";
-    sv.push_back(bigstring2.str());
-    sw.push_back(bigwire2.str());
-
-
-    BOOST_FOREACH(std::string s, sv) {
-        // also test << operator, which uses Element::str()
-        el = Element::createFromString(s);
-        EXPECT_EQ(s, el->str());
-        wire = el->toWire();
-        /*
-        std::cout << "Encoded wire format:" << std::endl;
-        my_print(wire);
-        std::cout << "Expecting:" << std::endl;
-        my_print(sw.at(0));
-        */
-        EXPECT_EQ(sw.at(0), wire);
-        sw.erase(sw.begin());
-        decoded_el = Element::fromWire(wire);
-        EXPECT_EQ(s, decoded_el->str());
-    }
-    
-    //EXPECT_THROW(Element::fromWire("Skan\004name\050\003foo"), DecodeError);
-    EXPECT_THROW(Element::fromWire("Skan\004name\050"), DecodeError);
-    EXPECT_THROW(Element::fromWire("Skan\004na"), DecodeError);
-    EXPECT_THROW(Element::fromWire("Skan\004name\050\003fo"), DecodeError);
-    EXPECT_NO_THROW(Element::fromWire("Skan\004name\041\003foo"));
-    EXPECT_THROW(Element::fromWire("Skan\004name\041\003fo"), DecodeError);
-    EXPECT_NO_THROW(Element::fromWire("Skan\004name\044\001a"));
-    EXPECT_THROW(Element::fromWire("Skab\004name\050\003foo"), DecodeError);
-
-    //EXPECT_EQ("\047\0031.2", Element::create(1.2)->toWire(0));
-    EXPECT_EQ("\046\0011", Element::createFromString("[ 1 ]")->toWire(1));
-
-    std::string ddef = "{\"data_specification\": {\"config_data\": [ {\"item_default\": \"Hello, world!\", \"item_name\": \"default_name\", \"item_optional\": False, \"item_type\": \"string\"}, {\"item_default\": [  ], \"item_name\": \"zone_list\", \"item_optional\": False, \"item_type\": \"list\", \"list_item_spec\": {\"item_name\": \"zone_name\", \"item_optional\": True, \"item_type\": \"string\"}} ], \"module_name\": \"Auth\"}}";
-    //std::string ddef = "{\"aaa\": 123, \"test\": [  ], \"zzz\": 123}";
-    ElementPtr ddef_el = Element::createFromString(ddef);
-    std::string ddef_wire = ddef_el->toWire();
-    ElementPtr ddef_el2 = Element::fromWire(ddef_wire);
-    std::string ddef2 = ddef_el2->str();
-    EXPECT_EQ(ddef, ddef2);
+    // Wire format is now plain JSON.
+    ElementPtr el;
+    EXPECT_EQ("1", Element::create(1)->toWire());
+    EXPECT_EQ("1.1", Element::create(1.1)->toWire());
+    EXPECT_EQ("true", Element::create(true)->toWire());
+    EXPECT_EQ("false", Element::create(false)->toWire());
+    EXPECT_EQ("null", Element::create()->toWire());
+    EXPECT_EQ("\"a string\"", Element::create("a string")->toWire());
+    EXPECT_EQ("[ \"a\", \"list\" ]", Element::fromJSON("[ \"a\", \"list\" ]")->toWire());
+    EXPECT_EQ("{ \"a\": \"map\" }", Element::fromJSON("{ \"a\": \"map\" }")->toWire());
+
+    EXPECT_EQ("1", Element::fromWire("1")->str());
+
+    std::stringstream ss;
+    ss << "1";
+    EXPECT_EQ("1", Element::fromWire(ss, 1)->str());
+
+    // Some malformed JSON input
+    EXPECT_THROW(Element::fromJSON("{\":"), isc::data::JSONError);
+    EXPECT_THROW(Element::fromJSON("]"), isc::data::JSONError);
+    EXPECT_THROW(Element::fromJSON("[ 1, 2, }"), isc::data::JSONError);
 }
 
-ElementPtr efs(const std::string& str) {
-    return Element::createFromString(str);
+static ElementPtr
+efs(const std::string& str) {
+    return Element::fromJSON(str);
 }
 
 TEST(Element, equals) {
     // why does EXPECT_EQ not work?
-    EXPECT_TRUE(efs("1") == efs("1"));
-    EXPECT_FALSE(efs("1") == efs("2"));
-    EXPECT_FALSE(efs("1") == efs("\"1\""));
-    EXPECT_FALSE(efs("1") == efs("[]"));
-    EXPECT_FALSE(efs("1") == efs("True"));
-    EXPECT_FALSE(efs("1") == efs("{}"));
-
-    EXPECT_TRUE(efs("1.1") == efs("1.1"));
-    EXPECT_FALSE(efs("1.0") == efs("1"));
-    EXPECT_FALSE(efs("1.1") == efs("\"1\""));
-    EXPECT_FALSE(efs("1.1") == efs("[]"));
-    EXPECT_FALSE(efs("1.1") == efs("True"));
-    EXPECT_FALSE(efs("1.1") == efs("{}"));
-
-    EXPECT_TRUE(efs("True") == efs("True"));
-    EXPECT_FALSE(efs("True") == efs("False"));
-    EXPECT_FALSE(efs("True") == efs("1"));
-    EXPECT_FALSE(efs("True") == efs("\"1\""));
-    EXPECT_FALSE(efs("True") == efs("[]"));
-    EXPECT_FALSE(efs("True") == efs("{}"));
-
-    EXPECT_TRUE(efs("\"foo\"") == efs("\"foo\""));
-    EXPECT_FALSE(efs("\"foo\"") == efs("\"bar\""));
-    EXPECT_FALSE(efs("\"foo\"") == efs("1"));
-    EXPECT_FALSE(efs("\"foo\"") == efs("\"1\""));
-    EXPECT_FALSE(efs("\"foo\"") == efs("True"));
-    EXPECT_FALSE(efs("\"foo\"") == efs("[]"));
-    EXPECT_FALSE(efs("\"foo\"") == efs("{}"));
-
-    EXPECT_TRUE(efs("[]") == efs("[]"));
-    EXPECT_TRUE(efs("[ 1, 2, 3 ]") == efs("[ 1, 2, 3 ]"));
-    EXPECT_TRUE(efs("[ \"a\", [ True, 1], 2.2 ]") == efs("[ \"a\", [ True, 1], 2.2 ]"));
-    EXPECT_FALSE(efs("[ \"a\", [ True, 1], 2.2 ]") == efs("[ \"a\", [ True, 2], 2.2 ]"));
-    EXPECT_FALSE(efs("[]") == efs("[1]"));
-    EXPECT_FALSE(efs("[]") == efs("1"));
-    EXPECT_FALSE(efs("[]") == efs("\"1\""));
-    EXPECT_FALSE(efs("[]") == efs("{}"));
-
-    EXPECT_TRUE(efs("{}") == efs("{}"));
-    EXPECT_TRUE(efs("{ \"foo\": \"bar\" }") == efs("{ \"foo\": \"bar\" }"));
-    EXPECT_TRUE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }") == efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"));
-    EXPECT_FALSE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }") == efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar2\" } }"));
-    EXPECT_FALSE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }") == efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\", 1 ], \"item3\": { \"foo\": \"bar\" } }"));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("1"));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("\"1\""));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("[]"));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("{}"));
+    EXPECT_EQ(efs("1"), efs("1"));
+    EXPECT_NE(efs("1"), efs("2"));
+    EXPECT_NE(efs("1"), efs("\"1\""));
+    EXPECT_NE(efs("1"), efs("[]"));
+    EXPECT_NE(efs("1"), efs("True"));
+    EXPECT_NE(efs("1"), efs("{}"));
+
+    EXPECT_EQ(efs("1.1"), efs("1.1"));
+    EXPECT_NE(efs("1.0"), efs("1"));
+    EXPECT_NE(efs("1.1"), efs("\"1\""));
+    EXPECT_NE(efs("1.1"), efs("[]"));
+    EXPECT_NE(efs("1.1"), efs("True"));
+    EXPECT_NE(efs("1.1"), efs("{}"));
+
+    EXPECT_EQ(efs("True"), efs("True"));
+    EXPECT_NE(efs("True"), efs("False"));
+    EXPECT_NE(efs("True"), efs("1"));
+    EXPECT_NE(efs("True"), efs("\"1\""));
+    EXPECT_NE(efs("True"), efs("[]"));
+    EXPECT_NE(efs("True"), efs("{}"));
+
+    EXPECT_EQ(efs("\"foo\""), efs("\"foo\""));
+    EXPECT_NE(efs("\"foo\""), efs("\"bar\""));
+    EXPECT_NE(efs("\"foo\""), efs("1"));
+    EXPECT_NE(efs("\"foo\""), efs("\"1\""));
+    EXPECT_NE(efs("\"foo\""), efs("True"));
+    EXPECT_NE(efs("\"foo\""), efs("[]"));
+    EXPECT_NE(efs("\"foo\""), efs("{}"));
+
+    EXPECT_EQ(efs("[]"), efs("[]"));
+    EXPECT_EQ(efs("[ 1, 2, 3 ]"), efs("[ 1, 2, 3 ]"));
+    EXPECT_EQ(efs("[ \"a\", [ True, 1], 2.2 ]"), efs("[ \"a\", [ True, 1], 2.2 ]"));
+    EXPECT_NE(efs("[ \"a\", [ True, 1], 2.2 ]"), efs("[ \"a\", [ True, 2], 2.2 ]"));
+    EXPECT_NE(efs("[]"), efs("[1]"));
+    EXPECT_NE(efs("[]"), efs("1"));
+    EXPECT_NE(efs("[]"), efs("\"1\""));
+    EXPECT_NE(efs("[]"), efs("{}"));
+
+    EXPECT_EQ(efs("{}"), efs("{}"));
+    EXPECT_EQ(efs("{ \"foo\": \"bar\" }"), efs("{ \"foo\": \"bar\" }"));
+    EXPECT_EQ(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"));
+    EXPECT_NE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar2\" } }"));
+    EXPECT_NE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\", 1 ], \"item3\": { \"foo\": \"bar\" } }"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("1"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("\"1\""));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("[]"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("{}"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("{ \"something\": \"different\" }"));
+
+    EXPECT_EQ(efs("null"), Element::create());
 }
 
 TEST(Element, removeIdentical) {
-    ElementPtr a = Element::createFromString("{}");
-    ElementPtr b = Element::createFromString("{}");
-    ElementPtr c = Element::createFromString("{}");
+    ElementPtr a = Element::createMap();
+    ElementPtr b = Element::createMap();
+    ElementPtr c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": 1 }");
-    b = Element::createFromString("{ \"a\": 1 }");
-    c = Element::createFromString("{}");
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    b = Element::createFromString("{}");
-    c = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::createMap();
+    c = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    b = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    c = Element::createFromString("{}");
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    b = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 3 ] }");
-    c = Element::createFromString("{ \"b\": [ 1, 2 ] }");
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 3 ] }");
+    c = Element::fromJSON("{ \"b\": [ 1, 2 ] }");
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{}");
-    c = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::createMap();
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    c = Element::createFromString("{}");
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{ \"a\": { \"b\": \"d\" } }");
-    c = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
+
+    EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)), TypeError);
 }
 
 TEST(Element, merge)
 {
-    ElementPtr a = Element::createFromString("{}");
-    ElementPtr b = Element::createFromString("{}");
-    ElementPtr c = Element::createFromString("{}");
+    ElementPtr a = Element::createMap();
+    ElementPtr b = Element::createMap();
+    ElementPtr c = Element::createMap();
     merge(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("1");
-    b = Element::createFromString("{}");
+    a = Element::fromJSON("1");
+    b = Element::createMap();
     EXPECT_THROW(merge(a, b), TypeError);
 
-    a = Element::createFromString("{}");
-    b = Element::createFromString("{ \"a\": 1 }");
-    c = Element::createFromString("{ \"a\": 1 }");
+    a = Element::createMap();
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::createMap();
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 2 }");
+    c = Element::fromJSON("{ \"a\": 2 }");
     merge(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 2 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
 
-    a = Element::createFromString("{ \"a\": 1 }");
-    b = Element::createFromString("{ \"a\": 2 }");
-    c = Element::createFromString("{ \"a\": 2 }");
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     merge(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
 
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{ \"a\": { \"b\": \"d\" } }");
-    c = Element::createFromString("{ \"a\": { \"b\": \"d\" } }");
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": null }");
+    c = Element::fromJSON("{  }");
     merge(a, b);
-    EXPECT_TRUE(a == c);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": null }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+    
+    // And some tests with multiple values
+    a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
+    c = Element::fromJSON("{ \"a\": 1, \"c\": \"a string\" }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
+    c = Element::fromJSON("{ \"a\": 1, \"b\": true }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
+    c = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
+    c = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
 
 }

+ 10 - 10
src/lib/cc/session.cc

@@ -63,7 +63,7 @@ namespace cc {
 
 class SessionImpl {
 public:
-    SessionImpl() : sequence_(-1) { queue_ = Element::createFromString("[]"); }
+    SessionImpl() : sequence_(-1) { queue_ = Element::createList(); }
     virtual ~SessionImpl() {}
     virtual void establish(const char& socket_file) = 0;
     virtual int getSocket() = 0;
@@ -73,7 +73,7 @@ public:
     virtual void readData(void* data, size_t datalen) = 0;
     virtual void startRead(boost::function<void()> user_handler) = 0;
     
-    int sequence_; // the next sequence number to use
+    long int sequence_; // the next sequence number to use
     std::string lname_;
     ElementPtr queue_;
 };
@@ -321,7 +321,7 @@ Session::establish(const char* socket_file) {
     // send a request for our local name, and wait for a response
     //
     ElementPtr get_lname_msg =
-        Element::createFromString("{ \"type\": \"getlname\" }");
+        Element::fromJSON("{ \"type\": \"getlname\" }");
     sendmsg(get_lname_msg);
 
     ElementPtr routing, msg;
@@ -429,7 +429,7 @@ Session::recvmsg(ElementPtr& env, ElementPtr& msg,
         msg = l_msg;
         return true;
     } else {
-        ElementPtr q_el = Element::createFromString("[]");
+        ElementPtr q_el = Element::createList();
         q_el->add(l_env);
         q_el->add(l_msg);
         impl_->queue_->add(q_el);
@@ -440,7 +440,7 @@ Session::recvmsg(ElementPtr& env, ElementPtr& msg,
 
 void
 Session::subscribe(std::string group, std::string instance) {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+    ElementPtr env = Element::createMap();
 
     env->set("type", Element::create("subscribe"));
     env->set("group", Element::create(group));
@@ -451,7 +451,7 @@ Session::subscribe(std::string group, std::string instance) {
 
 void
 Session::unsubscribe(std::string group, std::string instance) {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+    ElementPtr env = Element::createMap();
 
     env->set("type", Element::create("unsubscribe"));
     env->set("group", Element::create(group));
@@ -464,8 +464,8 @@ int
 Session::group_sendmsg(ElementPtr msg, std::string group,
                        std::string instance, std::string to)
 {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
-    int nseq = ++impl_->sequence_;
+    ElementPtr env = Element::createMap();
+    long int nseq = ++impl_->sequence_;
     
     env->set("type", Element::create("send"));
     env->set("from", Element::create(impl_->lname_));
@@ -488,8 +488,8 @@ Session::group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
 
 int
 Session::reply(ElementPtr& envelope, ElementPtr& newmsg) {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
-    int nseq = ++impl_->sequence_;
+    ElementPtr env = Element::createMap();
+    long int nseq = ++impl_->sequence_;
     
     env->set("type", Element::create("send"));
     env->set("from", Element::create(impl_->lname_));

+ 11 - 11
src/lib/config/ccsession.cc

@@ -47,7 +47,7 @@ using namespace std;
 
 using isc::data::Element;
 using isc::data::ElementPtr;
-using isc::data::ParseError;
+using isc::data::JSONError;
 
 namespace isc {
 namespace config {
@@ -56,7 +56,7 @@ namespace config {
 ElementPtr
 createAnswer()
 {
-    ElementPtr answer = Element::createFromString("{\"result\": [] }");
+    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
     ElementPtr answer_content = answer->get("result");
     answer_content->add(Element::create(0));
     return answer;
@@ -68,7 +68,7 @@ createAnswer(const int rcode, const ElementPtr arg)
     if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
         isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
     }
-    ElementPtr answer = Element::createFromString("{\"result\": [] }");
+    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
     ElementPtr answer_content = answer->get("result");
     answer_content->add(Element::create(rcode));
     answer_content->add(arg);
@@ -78,7 +78,7 @@ createAnswer(const int rcode, const ElementPtr arg)
 ElementPtr
 createAnswer(const int rcode, const std::string& arg)
 {
-    ElementPtr answer = Element::createFromString("{\"result\": [] }");
+    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
     ElementPtr answer_content = answer->get("result");
     answer_content->add(Element::create(rcode));
     answer_content->add(Element::create(arg));
@@ -125,8 +125,8 @@ createCommand(const std::string& command)
 ElementPtr
 createCommand(const std::string& command, ElementPtr arg)
 {
-    ElementPtr cmd = Element::createFromString("{}");
-    ElementPtr cmd_parts = Element::createFromString("[]");
+    ElementPtr cmd = Element::createMap();
+    ElementPtr cmd_parts = Element::createList();
     cmd_parts->add(Element::create(command));
     if (arg) {
         cmd_parts->add(arg);
@@ -175,7 +175,7 @@ ModuleCCSession::readModuleSpecification(const std::string& filename) {
 
     try {
         module_spec = moduleSpecFromFile(file, true);
-    } catch (ParseError pe) {
+    } catch (JSONError pe) {
         cout << "Error parsing module specification file: " << pe.what() << endl;
         exit(1);
     } catch (ModuleSpecError dde) {
@@ -252,10 +252,10 @@ ModuleCCSession::init(
         std::cerr << "[" << module_name_ << "] Error in specification: " << answer << std::endl;
     }
     
-    setLocalConfig(Element::createFromString("{}"));
+    setLocalConfig(Element::fromJSON("{}"));
     // get any stored configuration from the manager
     if (config_handler_) {
-        ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
+        ElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
         seq = session_.group_sendmsg(cmd, "ConfigManager");
         session_.group_recvmsg(env, answer, false, seq);
         ElementPtr new_config = parseAnswer(rcode, answer);
@@ -274,7 +274,7 @@ ElementPtr
 ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
 {
     ElementPtr answer;
-    ElementPtr errors = Element::createFromString("[]");
+    ElementPtr errors = Element::createList();
     if (!config_handler_) {
         answer = createAnswer(1, module_name_ + " does not have a config handler");
     } else if (!module_specification_.validate_config(new_config, false, errors)) {
@@ -363,7 +363,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name)
     session_.subscribe(module_name);
 
     // Get the current configuration values for that module
-    ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
+    ElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
     ElementPtr env, answer;
     int rcode;
     

+ 19 - 2
src/lib/config/config_data.cc

@@ -26,6 +26,16 @@ using namespace isc::data;
 namespace isc {
 namespace config {
 
+//
+// Return a part of a specification, as identified by the
+// '/'-separated identifier.
+// If it cannot be found, a DataNotFound error is thrown.
+//
+// Recursively goes through the Element. If it is a List,
+// we search it contents to have 'items' (i.e. contain item_name)
+// If it is a map, we search through the list contained in its
+// 'map_item_spec' value. This code assumes the data has been
+// validated and conforms to the specification.
 static ElementPtr
 find_spec_part(ElementPtr spec, const std::string& identifier)
 {
@@ -97,6 +107,11 @@ find_spec_part(ElementPtr spec, const std::string& identifier)
     return spec_part;
 }
 
+//
+// Adds the names of the items in the given specification part.
+// If recurse is true, maps will also have their children added.
+// Result must be a ListElement
+//
 static void
 spec_name_list(ElementPtr result, ElementPtr spec_part, std::string prefix, bool recurse = false)
 {
@@ -129,6 +144,8 @@ spec_name_list(ElementPtr result, ElementPtr spec_part, std::string prefix, bool
 ElementPtr
 ConfigData::getValue(const std::string& identifier)
 {
+    // 'fake' is set, but dropped by this function and
+    // serves no further purpose.
     bool fake;
     return getValue(fake, identifier);
 }
@@ -158,7 +175,7 @@ ConfigData::getValue(bool& is_default, const std::string& identifier)
 ElementPtr
 ConfigData::getItemList(const std::string& identifier, bool recurse)
 {
-    ElementPtr result = Element::createFromString("[]");
+    ElementPtr result = Element::createList();
     ElementPtr spec_part = getModuleSpec().getConfigSpec();
     if (identifier != "" && identifier != "/") {
         spec_part = find_spec_part(spec_part, identifier);
@@ -172,7 +189,7 @@ ConfigData::getItemList(const std::string& identifier, bool recurse)
 ElementPtr
 ConfigData::getFullConfig()
 {
-    ElementPtr result = Element::createFromString("{}");
+    ElementPtr result = Element::createMap();
     ElementPtr items = getItemList("", true);
     BOOST_FOREACH(ElementPtr item, items->listValue()) {
         result->set(item->stringValue(), getValue(item->stringValue()));

+ 2 - 2
src/lib/config/config_data.h

@@ -39,12 +39,12 @@ class ConfigData {
 public:
     /// Constructs a ConfigData option with no specification and an
     /// empty configuration.
-    ConfigData() { _config = Element::createFromString("{}"); };
+    ConfigData() { _config = Element::createMap(); };
     
     /// Constructs a ConfigData option with the given specification
     /// and an empty configuration.
     /// \param module_spec A ModuleSpec for the relevant module
-    ConfigData(const ModuleSpec& module_spec) : _module_spec(module_spec) { _config = Element::createFromString("{}"); }
+    ConfigData(const ModuleSpec& module_spec) : _module_spec(module_spec) { _config = Element::createMap(); }
 
     virtual ~ConfigData() {};
 

+ 13 - 52
src/lib/config/module_spec.cc

@@ -31,49 +31,6 @@ namespace config {
 // static functions
 //
 
-// todo: is there a direct way to get a std::string from an enum label?
-static std::string
-getType_string(Element::types type)
-{
-    switch(type) {
-    case Element::integer:
-        return std::string("integer");
-    case Element::real:
-        return std::string("real");
-    case Element::boolean:
-        return std::string("boolean");
-    case Element::string:
-        return std::string("string");
-    case Element::list:
-        return std::string("list");
-    case Element::map:
-        return std::string("map");
-    default:
-        return std::string("unknown");
-    }
-}
-
-static Element::types
-getType_value(const std::string& type_name) {
-    if (type_name == "integer") {
-        return Element::integer;
-    } else if (type_name == "real") {
-        return Element::real;
-    } else if (type_name == "boolean") {
-        return Element::boolean;
-    } else if (type_name == "string") {
-        return Element::string;
-    } else if (type_name == "list") {
-        return Element::list;
-    } else if (type_name == "map") {
-        return Element::map;
-    } else if (type_name == "any") {
-        return Element::any;
-    } else {
-        throw ModuleSpecError(type_name + " is not a valid type name");
-    }
-}
-
 static void
 check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types type, bool mandatory)
 {
@@ -81,7 +38,7 @@ check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types
         if (spec->get(name)->getType() == type) {
             return;
         } else {
-            throw ModuleSpecError(name + " not of type " + getType_string(type));
+            throw ModuleSpecError(name + " not of type " + Element::typeToName(type));
         }
     } else if (mandatory) {
         // todo: want parent item name, and perhaps some info about location
@@ -99,17 +56,17 @@ check_config_item(const ElementPtr& spec) {
     check_leaf_item(spec, "item_type", Element::string, true);
     check_leaf_item(spec, "item_optional", Element::boolean, true);
     check_leaf_item(spec, "item_default",
-                    getType_value(spec->get("item_type")->stringValue()),
+                    Element::nameToType(spec->get("item_type")->stringValue()),
                     !spec->get("item_optional")->boolValue()
                    );
 
     // if list, check the list specification
-    if (getType_value(spec->get("item_type")->stringValue()) == Element::list) {
+    if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::list) {
         check_leaf_item(spec, "list_item_spec", Element::map, true);
         check_config_item(spec->get("list_item_spec"));
     }
     // todo: add stuff for type map
-    if (getType_value(spec->get("item_type")->stringValue()) == Element::map) {
+    if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::map) {
         check_leaf_item(spec, "map_item_spec", Element::list, true);
         check_config_item_list(spec->get("map_item_spec"));
     }
@@ -161,7 +118,11 @@ check_data_specification(const ElementPtr& spec) {
 static void
 check_module_specification(const ElementPtr& def)
 {
-    check_data_specification(def);
+    try {
+        check_data_specification(def);
+    } catch (TypeError te) {
+        throw ModuleSpecError(te.what());
+    }
 }
 
 //
@@ -231,7 +192,7 @@ ModuleSpec::validate_config(const ElementPtr data, const bool full, ElementPtr e
 
 ModuleSpec
 moduleSpecFromFile(const std::string& file_name, const bool check)
-                   throw(ParseError, ModuleSpecError)
+                   throw(JSONError, ModuleSpecError)
 {
     std::ifstream file;
 
@@ -242,7 +203,7 @@ moduleSpecFromFile(const std::string& file_name, const bool check)
         throw ModuleSpecError(errs.str());
     }
 
-    ElementPtr module_spec_element = Element::createFromString(file, file_name);
+    ElementPtr module_spec_element = Element::fromJSON(file, file_name);
     if (module_spec_element->contains("module_spec")) {
         return ModuleSpec(module_spec_element->get("module_spec"), check);
     } else {
@@ -252,8 +213,8 @@ moduleSpecFromFile(const std::string& file_name, const bool check)
 
 ModuleSpec
 moduleSpecFromFile(std::ifstream& in, const bool check)
-                   throw(ParseError, ModuleSpecError) {
-    ElementPtr module_spec_element = Element::createFromString(in);
+                   throw(JSONError, ModuleSpecError) {
+    ElementPtr module_spec_element = Element::fromJSON(in);
     if (module_spec_element->contains("module_spec")) {
         return ModuleSpec(module_spec_element->get("module_spec"), check);
     } else {

+ 2 - 2
src/lib/config/module_spec.h

@@ -110,7 +110,7 @@ namespace isc { namespace config {
     /// is checked to be of the correct form
     ModuleSpec
     moduleSpecFromFile(const std::string& file_name, const bool check = true)
-                       throw(ParseError, ModuleSpecError);
+                       throw(JSONError, ModuleSpecError);
 
     /// Creates a \c ModuleSpec instance from the given input
     /// stream that contains the contents of a .spec file.
@@ -122,7 +122,7 @@ namespace isc { namespace config {
     /// to be of the correct form
     ModuleSpec
     moduleSpecFromFile(std::ifstream& in, const bool check = true)
-                       throw(ParseError, ModuleSpecError);
+                       throw(JSONError, ModuleSpecError);
 } }
 
 #endif // _DATA_DEF_H

File diff suppressed because it is too large
+ 40 - 40
src/lib/config/tests/ccsession_unittests.cc


+ 18 - 18
src/lib/config/tests/config_data_unittests.cc

@@ -62,9 +62,9 @@ TEST(ConfigData, getValue) {
     EXPECT_EQ("b", cd.getValue(is_default, "value5")->get(1)->stringValue());
     EXPECT_EQ("b", cd.getValue(is_default, "value5/")->get(1)->stringValue());
     EXPECT_TRUE(is_default);
-    EXPECT_EQ("{}", cd.getValue("value6")->str());
-    EXPECT_EQ("{}", cd.getValue(is_default, "value6")->str());
-    EXPECT_EQ("{}", cd.getValue(is_default, "value6/")->str());
+    EXPECT_EQ("{  }", cd.getValue("value6")->str());
+    EXPECT_EQ("{  }", cd.getValue(is_default, "value6")->str());
+    EXPECT_EQ("{  }", cd.getValue(is_default, "value6/")->str());
     EXPECT_TRUE(is_default);
     EXPECT_EQ("[  ]", cd.getValue("value8")->str());
 
@@ -87,34 +87,34 @@ TEST(ConfigData, setLocalConfig) {
     ConfigData cd = ConfigData(spec2);
     bool is_default;
 
-    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
-    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
+    ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
 
-    EXPECT_EQ("{}", cd.getValue("item6")->str());
+    EXPECT_EQ("{  }", cd.getValue("item6")->str());
 
     cd.setLocalConfig(my_config);
     EXPECT_EQ(2, cd.getValue(is_default, "item1")->intValue());
     EXPECT_FALSE(is_default);
-    EXPECT_EQ("{}", cd.getValue("item6")->str());
+    EXPECT_EQ("{  }", cd.getValue("item6")->str());
     EXPECT_EQ(1.1, cd.getValue(is_default, "item2")->doubleValue());
     EXPECT_TRUE(is_default);
 
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{\"value1\": \"a\"}", cd.getValue("item6")->str());
+    EXPECT_EQ("{ \"value1\": \"a\" }", cd.getValue("item6")->str());
 }
 
 TEST(ConfigData, getLocalConfig) {
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
-    EXPECT_EQ("{}", cd.getLocalConfig()->str());
+    EXPECT_EQ("{  }", cd.getLocalConfig()->str());
     
-    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
+    ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
     cd.setLocalConfig(my_config);
-    EXPECT_EQ("{\"item1\": 2}", cd.getLocalConfig()->str());
+    EXPECT_EQ("{ \"item1\": 2 }", cd.getLocalConfig()->str());
 
-    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{\"item6\": {\"value1\": \"a\"}}", cd.getLocalConfig()->str());
+    EXPECT_EQ("{ \"item6\": { \"value1\": \"a\" } }", cd.getLocalConfig()->str());
 }
 
 TEST(ConfigData, getItemList) {
@@ -130,12 +130,12 @@ TEST(ConfigData, getFullConfig) {
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
 
-    EXPECT_EQ("{\"item1\": 1, \"item2\": 1.1, \"item3\": True, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None}", cd.getFullConfig()->str());
-    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
+    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
     cd.setLocalConfig(my_config);
-    EXPECT_EQ("{\"item1\": 2, \"item2\": 1.1, \"item3\": True, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None}", cd.getFullConfig()->str());
-    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    EXPECT_EQ("{ \"item1\": 2, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{\"item1\": 1, \"item2\": 1.1, \"item3\": True, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"a\", \"item6/value2\": None}", cd.getFullConfig()->str());
+    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"a\", \"item6/value2\": None }", cd.getFullConfig()->str());
 }
 

+ 8 - 8
src/lib/config/tests/fake_session.cc

@@ -92,12 +92,12 @@ getFirstMessage(std::string& group, std::string& to)
 void
 addMessage(ElementPtr msg, const std::string& group, const std::string& to)
 {
-    ElementPtr m_el = Element::createFromString("[]");
+    ElementPtr m_el = Element::createList();
     m_el->add(Element::create(group));
     m_el->add(Element::create(to));
     m_el->add(msg);
     if (!msg_queue) {
-        msg_queue = Element::createFromString("[]");
+        msg_queue = Element::createList();
     }
     msg_queue->add(m_el);
 }
@@ -108,8 +108,8 @@ haveSubscription(const std::string& group, const std::string& instance)
     if (!subscriptions) {
         return false;
     }
-    ElementPtr s1 = Element::createFromString("[]");
-    ElementPtr s2 = Element::createFromString("[]");
+    ElementPtr s1 = Element::createList();
+    ElementPtr s2 = Element::createList();
     s1->add(Element::create(group));
     s1->add(Element::create(instance));
     s2->add(Element::create(group));
@@ -206,7 +206,7 @@ Session::recvmsg(ElementPtr& env, ElementPtr& msg, bool nonblock UNUSED_PARAM, i
         BOOST_FOREACH(ElementPtr c_m, msg_queue->listValue()) {
             ElementPtr to_remove = ElementPtr();
             if (haveSubscription(c_m->get(0), c_m->get(1))) {
-                env = Element::createFromString("{}");
+                env = Element::createMap();
                 env->set("group", c_m->get(0));
                 env->set("to", c_m->get(1));
                 msg = c_m->get(2);
@@ -226,11 +226,11 @@ Session::recvmsg(ElementPtr& env, ElementPtr& msg, bool nonblock UNUSED_PARAM, i
 void
 Session::subscribe(std::string group, std::string instance) {
     //cout << "[XX] client subscribes to " << group << " . " << instance << endl;
-    ElementPtr s_el = Element::createFromString("[]");
+    ElementPtr s_el = Element::createList();
     s_el->add(Element::create(group));
     s_el->add(Element::create(instance));
     if (!subscriptions) {
-        subscriptions = Element::createFromString("[]");
+        subscriptions = Element::createList();
     }
     subscriptions->add(s_el);
 }
@@ -238,7 +238,7 @@ Session::subscribe(std::string group, std::string instance) {
 void
 Session::unsubscribe(std::string group, std::string instance) {
     //cout << "[XX] client unsubscribes from " << group << " . " << instance << endl;
-    ElementPtr s_el = Element::createFromString("[]");
+    ElementPtr s_el = Element::createList();
     s_el->add(Element::create(group));
     s_el->add(Element::create(instance));
     if (!subscriptions) {

+ 11 - 11
src/lib/config/tests/module_spec_unittests.cc

@@ -58,7 +58,7 @@ TEST(ModuleSpec, ReadingSpecfiles) {
                    ": No such file or directory");
 
     dd = moduleSpecFromFile(specfile("spec2.spec"));
-    EXPECT_EQ("[ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ]", dd.getCommandsSpec()->str());
+    EXPECT_EQ("[ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ]", dd.getCommandsSpec()->str());
     EXPECT_EQ("Spec2", dd.getModuleName());
     EXPECT_EQ("", dd.getModuleDescription());
 
@@ -82,13 +82,13 @@ TEST(ModuleSpec, ReadingSpecfiles) {
 
 TEST(ModuleSpec, SpecfileItems) {
     module_spec_error("spec3.spec",
-                   "item_name missing in {\"item_default\": 1, \"item_optional\": False, \"item_type\": \"integer\"}");
+                   "item_name missing in { \"item_default\": 1, \"item_optional\": false, \"item_type\": \"integer\" }");
     module_spec_error("spec4.spec",
-                   "item_type missing in {\"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": False}");
+                   "item_type missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false }");
     module_spec_error("spec5.spec",
-                   "item_optional missing in {\"item_default\": 1, \"item_name\": \"item1\", \"item_type\": \"integer\"}");
+                   "item_optional missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_type\": \"integer\" }");
     module_spec_error("spec6.spec",
-                   "item_default missing in {\"item_name\": \"item1\", \"item_optional\": False, \"item_type\": \"integer\"}");
+                   "item_default missing in { \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }");
     module_spec_error("spec9.spec",
                    "item_default not of type integer");
     module_spec_error("spec10.spec",
@@ -108,7 +108,7 @@ TEST(ModuleSpec, SpecfileItems) {
 TEST(ModuleSpec, SpecfileConfigData)
 {
     module_spec_error("spec7.spec",
-                   "module_name missing in {}");
+                   "module_name missing in {  }");
     module_spec_error("spec8.spec",
                    "No module_spec in specification");
     module_spec_error("spec16.spec",
@@ -120,9 +120,9 @@ TEST(ModuleSpec, SpecfileConfigData)
 TEST(ModuleSpec, SpecfileCommands)
 {
     module_spec_error("spec17.spec",
-                   "command_name missing in {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\"}");
+                   "command_name missing in { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\" }");
     module_spec_error("spec18.spec",
-                   "command_args missing in {\"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}");
+                   "command_args missing in { \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }");
     module_spec_error("spec19.spec",
                    "command_args not of type list");
     module_spec_error("spec20.spec",
@@ -139,7 +139,7 @@ data_test(ModuleSpec dd, const std::string& data_file_name)
     std::ifstream data_file;
 
     data_file.open(specfile(data_file_name).c_str());
-    ElementPtr data = Element::createFromString(data_file, data_file_name);
+    ElementPtr data = Element::fromJSON(data_file, data_file_name);
     data_file.close();
 
     return dd.validate_config(data);
@@ -151,7 +151,7 @@ data_test_with_errors(ModuleSpec dd, const std::string& data_file_name, ElementP
     std::ifstream data_file;
 
     data_file.open(specfile(data_file_name).c_str());
-    ElementPtr data = Element::createFromString(data_file, data_file_name);
+    ElementPtr data = Element::fromJSON(data_file, data_file_name);
     data_file.close();
 
     return dd.validate_config(data, true, errors);
@@ -169,7 +169,7 @@ TEST(ModuleSpec, DataValidation) {
     EXPECT_TRUE(data_test(dd, "data22_7.data"));
     EXPECT_FALSE(data_test(dd, "data22_8.data"));
 
-    ElementPtr errors = Element::createFromString("[]");
+    ElementPtr errors = Element::createList();
     EXPECT_FALSE(data_test_with_errors(dd, "data22_8.data", errors));
     EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
 }

+ 1 - 1
src/lib/datasrc/tests/datasrc_unittest.cc

@@ -49,7 +49,7 @@ using namespace isc::datasrc;
 using namespace isc::data;
 
 namespace {
-const ElementPtr SQLITE_DBFILE_EXAMPLE = Element::createFromString(
+const ElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/example.org.sqlite3\"}");
 
 class DataSrcTest : public ::testing::Test {

+ 6 - 6
src/lib/datasrc/tests/sqlite3_unittest.cc

@@ -43,22 +43,22 @@ using namespace isc::datasrc;
 using namespace isc::data;
 
 namespace {
-ElementPtr SQLITE_DBFILE_EXAMPLE = Element::createFromString(
+ElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::createFromString(
+ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::createFromString(
+ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_BROKENDB = Element::createFromString(
+ElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_MEMORY = Element::createFromString(
+ElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON(
     "{ \"database_file\": \":memory:\"}");
 
 // The following file must be non existent and must be non"creatable";
 // the sqlite3 library will try to create a new DB file if it doesn't exist,
 // so to test a failure case the create operation should also fail.
 // The "nodir", a non existent directory, is inserted for this purpose.
-ElementPtr SQLITE_DBFILE_NOTEXIST = Element::createFromString(
+ElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}");
 
 const string sigdata_common(" 20100322084538 20100220084538 "

+ 16 - 211
src/lib/python/isc/cc/message.py

@@ -13,224 +13,29 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+#
+# Functions for reading and parsing cc messages
+# Currently these are only abstraction functions for JSON conversion.
+#
+
 import sys
 import struct
 
-class DecodeError(Exception): pass
-
-PROTOCOL_VERSION = 0x536b616e
-
-_ITEM_BLOB = 0x01
-_ITEM_HASH = 0x02
-_ITEM_LIST = 0x03
-_ITEM_NULL = 0x04
-_ITEM_BOOL = 0x05
-_ITEM_INT  = 0x06
-_ITEM_REAL = 0x07
-_ITEM_UTF8 = 0x08
-_ITEM_MASK = 0x0f
-
-_ITEM_LENGTH_32   = 0x00
-_ITEM_LENGTH_16   = 0x10
-_ITEM_LENGTH_8    = 0x20
-_ITEM_LENGTH_MASK = 0x30
+import json
 
 def to_wire(items):
-    """Encode a dict into wire format.
-    >>> wire_format = Message.to_wire({"a": "b"})
-    """
-    return struct.pack(">I", PROTOCOL_VERSION) + _encode_hash(items)
-
-def _encode_tag(tag):
-    """Encode a single UTF-8 tag.
-    ... wire_partial = Message._encode_tag('this')
-    """
-    binary = bytes(tag, 'utf-8')
-    if len(binary) > 255:
-        raise ArgumentError("tag is too long (max 255 encoded bytes)")
-    return(struct.pack(">B", len(binary))) + binary
-
-def _encode_length_and_type(data, datatype):
-    """Helper method to handle the length encoding in one place."""
-    if data == None:
-        return(struct.pack(">B", _ITEM_NULL))
-    length = len(data)
-    if length < 0x0000100:
-        return(struct.pack(">B B", datatype | _ITEM_LENGTH_8, length) + data)
-    elif length < 0x00010000:
-        return(struct.pack(">B H", datatype | _ITEM_LENGTH_16, length) + data)
-    else:
-        return(struct.pack(">B I", datatype | _ITEM_LENGTH_32, length) + data)
-
-def _pack_utf8(item):
-    """Pack a string (utf-8) and its type/length prefix."""
-    return (_encode_length_and_type(bytes(item, 'utf-8'), _ITEM_UTF8))
-
-def _pack_blob(item):
-    """Pack a blob (binary data) and its type/length prefix."""
-    return (_encode_length_and_type(item, _ITEM_BLOB))
-
-def _pack_bool(item):
-    """Pack a bool and its type/length prefix."""
-    return (_encode_length_and_type(_encode_bool(item), _ITEM_BOOL))
-
-def _pack_int(item):
-    """Pack an integer and its type/length prefix."""
-    return (_encode_length_and_type(bytes(str(item), 'utf-8'), _ITEM_INT))
-
-def _pack_real(item):
-    """Pack an integer and its type/length prefix."""
-    return (_encode_length_and_type(bytes(str(item), 'utf-8'), _ITEM_REAL))
-
-def _pack_array(item):
-    """Pack a list (array) and its type/length prefix."""
-    return (_encode_length_and_type(_encode_array(item), _ITEM_LIST))
-
-def _pack_hash(item):
-    """Pack a dict (hash) and its type/length prefix."""
-    data = _encode_hash(item)
-    return (_encode_length_and_type(data, _ITEM_HASH))
-
-def _pack_nil():
-    """Encode a nil (NULL, None) item."""
-    return _encode_length_and_type(None, None)
-
-def _encode_item(item):
-    """Encode each item depending on its type"""
-    if item == None:
-        return (_pack_nil())
-    elif type(item) == bool:
-        return (_pack_bool(item))
-    elif type(item) == int:
-        return (_pack_int(item))
-    elif type(item) == float:
-        return (_pack_real(item))
-    elif type(item) == dict:
-        return (_pack_hash(item))
-    elif type(item) == list:
-        return (_pack_array(item))
-    elif type(item) in (bytes, bytearray):
-        return (_pack_blob(item))
-    else:
-        return (_pack_utf8(str(item)))
-
-def _encode_bool(item):
-    """Encode a boolean value into a bytearray of one byte (0=false)"""
-    if item:
-        return b'1'
-    else:
-        return b'0'
-
-def _encode_array(item):
-    """Encode an array, where each value is encoded recursively"""
-    ret = bytes()
-    for i in item:
-        ret += _encode_item(i)
-    return ret
-
-def _encode_hash(item):
-    """Encode a hash, where each value is encoded recursively"""
-    ret = bytes()
-    for key, value in item.items():
-        ret += _encode_tag(key)
-        ret += _encode_item(value)
-    return ret
-
-#
-# decode methods
-#
+    '''Encodes the given python structure in JSON, and converts the
+       result to bytes. Raises a TypeError if the given structure is
+       not serializable with JSON.'''
+    return json.dumps(items).encode('utf8')
 
 def from_wire(data):
-    if len(data) < 5:
-        raise DecodeError("Data is too short to decode")
-    wire_version, data = data[0:4], data[4:]
-    wire_version = struct.unpack(">I", wire_version)[0]
-    if wire_version != PROTOCOL_VERSION:
-        raise DecodeError("Incorrect protocol version")
-    return _decode_hash(data)
-
-def _decode_tag(data):
-    if len(data) < 1:
-        raise DecodeError("Data underrun while decoding")
-    length = data[0]
-    if len(data) - 1 < length:
-        raise DecodeError("Data underrun while decoding")
-    return [data[1:length + 1].decode(), data[length + 1:]]
-
-def _decode_item(data):
-    if len(data) < 1:
-        raise DecodeError("Data underrun while decoding")
-    type_and_length_format = data[0]
-    item_type = type_and_length_format & _ITEM_MASK
-    length_format = type_and_length_format & _ITEM_LENGTH_MASK
-
-    if item_type == _ITEM_NULL:
-        data = data[1:]
-    else:
-        if length_format == _ITEM_LENGTH_8:
-            if len(data) - 1 < 1:
-                raise DecodeError("Data underrun while decoding")
-            length = data[1]
-            data = data[2:]
-        elif length_format == _ITEM_LENGTH_16:
-            if len(data) - 1 < 2:
-                raise DecodeError("Data underrun while decoding")
-            length = struct.unpack(">H", data[1:3])[0]
-            data = data[3:]
-        elif length_format == _ITEM_LENGTH_32:
-            if len(data) - 1 < 4:
-                raise DecodeError("Data underrun while decoding")
-            length = struct.unpack(">I", data[1:5])[0]
-            data = data[5:]
-        if len(data) < length:
-            raise DecodeError("Data underrun while decoding")
-        item = data[0:length]
-        data = data[length:]
-
-    if item_type == _ITEM_BLOB:
-        value = item
-    elif item_type == _ITEM_BOOL:
-        value = _decode_bool(item)
-    elif item_type == _ITEM_INT:
-        value = _decode_int(item)
-    elif item_type == _ITEM_REAL:
-        value = _decode_real(item)
-    elif item_type == _ITEM_UTF8:
-        value = str(item, 'utf-8')
-    elif item_type == _ITEM_HASH:
-        value = _decode_hash(item)
-    elif item_type == _ITEM_LIST:
-        value = _decode_array(item)
-    elif item_type == _ITEM_NULL:
-        value = None
-    else:
-        raise DecodeError("Unknown item type in decode: %02x" % item_type)
-
-    return (value, data)
-
-def _decode_bool(data):
-    return data == b'1'
-
-def _decode_int(data):
-    return int(str(data, 'utf-8'))
-
-def _decode_real(data):
-    return float(str(data, 'utf-8'))
-
-def _decode_hash(data):
-    ret = {}
-    while len(data) > 0:
-        tag, data = _decode_tag(data)
-        value, data = _decode_item(data)
-        ret[tag] = value
-    return ret
-
-def _decode_array(data):
-    ret = []
-    while len(data) > 0:
-        value, data = _decode_item(data)
-        ret.append(value)
-    return ret
+    '''Decodes the given bytes and parses it with the builtin JSON
+       parser. Raises a ValueError if the data is not valid JSON.
+       Raises an AttributeError if the given object has no decode()
+       method (which should return a string).
+       '''
+    return json.loads(data.decode('utf8'))
 
 if __name__ == "__main__":
     import doctest

+ 1 - 1
src/lib/python/isc/cc/tests/Makefile.am

@@ -1,4 +1,4 @@
-PYTESTS = data_test.py session_test.py test.py
+PYTESTS = message_test.py data_test.py session_test.py
 # NOTE: test_session.py is to be run manually, so not automated.
 EXTRA_DIST = $(PYTESTS)
 EXTRA_DIST += sendcmd.py

+ 54 - 0
src/lib/python/isc/cc/tests/message_test.py

@@ -0,0 +1,54 @@
+
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# Tests for the functions in data.py
+#
+
+import unittest
+import isc.cc
+
+class MessageTest(unittest.TestCase):
+    def setUp(self):
+        self.msg1 = { "just": [ "an", "arbitrary", "structure" ] }
+        self.msg1_str = "{\"just\": [\"an\", \"arbitrary\", \"structure\"]}";
+        self.msg1_wire = self.msg1_str.encode()
+
+        self.msg2 = { "aaa": [ 1, 1.1, True, False, None ] }
+        self.msg2_str = "{\"aaa\": [1, 1.1, true, false, null]}";
+        self.msg2_wire = self.msg2_str.encode()
+
+    def test_encode_json(self):
+        self.assertEqual(self.msg1_wire, isc.cc.message.to_wire(self.msg1))
+        self.assertEqual(self.msg2_wire, isc.cc.message.to_wire(self.msg2))
+
+        self.assertRaises(TypeError, isc.cc.message.to_wire, NotImplemented)
+        
+    def test_decode_json(self):
+        self.assertEqual(self.msg1, isc.cc.message.from_wire(self.msg1_wire))
+        self.assertEqual(self.msg2, isc.cc.message.from_wire(self.msg2_wire))
+
+        self.assertRaises(AttributeError, isc.cc.message.from_wire, 1)
+        self.assertRaises(ValueError, isc.cc.message.from_wire, b'\x001')
+        self.assertRaises(ValueError, isc.cc.message.from_wire, b'')
+        self.assertRaises(ValueError, isc.cc.message.from_wire, b'{"a": ')
+        self.assertRaises(ValueError, isc.cc.message.from_wire, b'[ 1 ')
+        self.assertRaises(ValueError, isc.cc.message.from_wire, b']')
+
+if __name__ == '__main__':
+    unittest.main()
+
+

+ 51 - 51
src/lib/python/isc/cc/tests/session_test.py

@@ -39,10 +39,7 @@ class MySocket():
         pass
 
     def send(self, data):
-        #print("[XX] send called:")
-        #print(data)
         self.sendqueue.extend(data);
-        pass
 
     def readsent(self, length):
         if length > len(self.sendqueue):
@@ -70,7 +67,6 @@ class MySocket():
         return result
 
     def recv(self, length):
-        #print("[XX] recv(" + str(length) + ") of " + str(len(self.recvqueue)))
         if length > len(self.recvqueue):
             raise Exception("Buffer underrun in test, does the test provide the right data?")
         result = self.recvqueue[:length]
@@ -79,11 +75,19 @@ class MySocket():
         #print("[XX] queue now: " + str(self.recvqueue))
         return result
 
-    def addrecv(self, data):
-        self.recvqueue.extend(data)
-
-    def addrecvmsg(self, env, msg = None):
-        self.addrecv(self.preparemsg(env, msg))
+    def addrecv(self, env, msg = None):
+        if type(env) == dict:
+            env = isc.cc.message.to_wire(env)
+        if type(msg) == dict:
+            msg = isc.cc.message.to_wire(msg)
+        length = 2 + len(env);
+        if msg:
+            length += len(msg)
+        self.recvqueue.extend(struct.pack("!I", length))
+        self.recvqueue.extend(struct.pack("!H", len(env)))
+        self.recvqueue.extend(env)
+        if msg:
+            self.recvqueue.extend(msg)
 
 #
 # We subclass the Session class we're testing here, only
@@ -120,7 +124,7 @@ class testSession(unittest.TestCase):
         sess = MySession()
         sess.sendmsg({}, {"hello": "a"})
         sent = sess._socket.readsentmsg();
-        self.assertEqual(sent, b'\x00\x00\x00\x13\x00\x04SkanSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00\x12\x00\x02{}{"hello": "a"}')
         sess.close()
         self.assertRaises(SessionError, sess.sendmsg, {}, {"hello": "a"})
 
@@ -147,41 +151,37 @@ class testSession(unittest.TestCase):
     def test_session_recvmsg(self):
         sess = MySession()
         # {'to': "someone"}, {"hello": "a"}
-        self.recv_and_compare(sess,
-                              b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01a',
-                              {'to': "someone"}, {"hello": "a"})
+        #self.recv_and_compare(sess,
+        #                      b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01a',
+        #                      {'to': "someone"}, {"hello": "a"})
 
         # 'malformed' messages
         # shouldn't some of these raise exceptions?
-        self.recv_and_compare(sess, 
-                              b'\x00',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'\x00\x00\x00\x10',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'\x00\x00\x00\x02\x00\x00',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'\x00\x00\x00\x02\x00\x02',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'',
-                              None, None)
-
-        # incomplete data field
-        sess._socket.addrecv(b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01')
-        self.assertRaises(isc.cc.message.DecodeError, sess.recvmsg)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00\x00\x00\x10',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00\x00\x00\x02\x00\x00',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00\x00\x00\x02\x00\x02',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'',
+        #                      None, None)
+
         # need to clear
         sess._socket.recvqueue = bytearray()
         
         # 'queueing' system
         # sending message {'to': 'someone', 'reply': 1}, {"hello": "a"}
         #print("sending message {'to': 'someone', 'reply': 1}, {'hello': 'a'}")
-
         # get no message without asking for a specific sequence number reply
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {"hello": "a"})
         env, msg = sess.recvmsg(False)
         self.assertEqual(None, env)
         self.assertTrue(sess.has_queued_msgs())
@@ -193,7 +193,7 @@ class testSession(unittest.TestCase):
         # ask for a differe sequence number reply (that doesn't exist)
         # then ask for the one that is there
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {"hello": "a"})
         env, msg = sess.recvmsg(False, 2)
         self.assertEqual(None, env)
         self.assertEqual(None, msg)
@@ -206,7 +206,7 @@ class testSession(unittest.TestCase):
         # ask for a differe sequence number reply (that doesn't exist)
         # then ask for any message
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {"hello": "a"})
         env, msg = sess.recvmsg(False, 2)
         self.assertEqual(None, env)
         self.assertEqual(None, msg)
@@ -222,12 +222,12 @@ class testSession(unittest.TestCase):
         # send a new message, ask for specific message (get the first)
         # then ask for any message (get the second)
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {'hello': 'a'})
         env, msg = sess.recvmsg(False, 2)
         self.assertEqual(None, env)
         self.assertEqual(None, msg)
         self.assertTrue(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01b')
+        sess._socket.addrecv({'to': 'someone' }, {'hello': 'b'})
         env, msg = sess.recvmsg(False, 1)
         self.assertEqual({'to': 'someone', 'reply': 1 }, env)
         self.assertEqual({"hello": "a"}, msg)
@@ -241,8 +241,8 @@ class testSession(unittest.TestCase):
         # ask for that specific message (get the second)
         # then ask for any message (get the first)
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01b')
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone' }, {'hello': 'b'})
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {'hello': 'a'})
         env, msg = sess.recvmsg(False, 1)
         self.assertEqual({'to': 'someone', 'reply': 1}, env)
         self.assertEqual({"hello": "a"}, msg)
@@ -266,29 +266,29 @@ class testSession(unittest.TestCase):
         sess = MySession()
         sess.group_subscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x001\x00/Skan\x05group(\x07mygroup\x04type(\tsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00<\x00:{"group": "mygroup", "type": "subscribe", "instance": "*"}')
         
         sess.group_subscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x001\x00/Skan\x05group(\x07mygroup\x04type(\tsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00<\x00:{"group": "mygroup", "type": "subscribe", "instance": "*"}')
         
         sess.group_subscribe("mygroup", "my_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00;\x009Skan\x05group(\x07mygroup\x04type(\tsubscribe\x08instance(\x0bmy_instance')
+        self.assertEqual(sent, b'\x00\x00\x00F\x00D{"group": "mygroup", "type": "subscribe", "instance": "my_instance"}')
 
     def test_group_unsubscribe(self):
         sess = MySession()
         sess.group_unsubscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x003\x001Skan\x05group(\x07mygroup\x04type(\x0bunsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00>\x00<{"group": "mygroup", "type": "unsubscribe", "instance": "*"}')
         
         sess.group_unsubscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x003\x001Skan\x05group(\x07mygroup\x04type(\x0bunsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00>\x00<{"group": "mygroup", "type": "unsubscribe", "instance": "*"}')
         
         sess.group_unsubscribe("mygroup", "my_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00=\x00;Skan\x05group(\x07mygroup\x04type(\x0bunsubscribe\x08instance(\x0bmy_instance')
+        self.assertEqual(sent, b'\x00\x00\x00H\x00F{"group": "mygroup", "type": "unsubscribe", "instance": "my_instance"}')
 
     def test_group_sendmsg(self):
         sess = MySession()
@@ -296,17 +296,17 @@ class testSession(unittest.TestCase):
 
         sess.group_sendmsg({ 'hello': 'a' }, "my_group")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00W\x00HSkan\x04from(\ttest_name\x03seq&\x012\x02to(\x01*\x08instance(\x01*\x05group(\x08my_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00p\x00`{"from": "test_name", "seq": 2, "to": "*", "instance": "*", "group": "my_group", "type": "send"}{"hello": "a"}')
         self.assertEqual(sess._sequence, 2)
 
         sess.group_sendmsg({ 'hello': 'a' }, "my_group", "my_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00a\x00RSkan\x04from(\ttest_name\x03seq&\x013\x02to(\x01*\x08instance(\x0bmy_instance\x05group(\x08my_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00z\x00j{"from": "test_name", "seq": 3, "to": "*", "instance": "my_instance", "group": "my_group", "type": "send"}{"hello": "a"}')
         self.assertEqual(sess._sequence, 3)
         
         sess.group_sendmsg({ 'hello': 'a' }, "your_group", "your_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00e\x00VSkan\x04from(\ttest_name\x03seq&\x014\x02to(\x01*\x08instance(\ryour_instance\x05group(\nyour_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00~\x00n{"from": "test_name", "seq": 4, "to": "*", "instance": "your_instance", "group": "your_group", "type": "send"}{"hello": "a"}')
         self.assertEqual(sess._sequence, 4)
 
     def test_group_recvmsg(self):
@@ -318,11 +318,11 @@ class testSession(unittest.TestCase):
         sess = MySession()
         sess.group_reply({ 'from': 'me', 'group': 'our_group', 'instance': 'other_instance', 'seq': 4}, {"hello": "a"})
         sent = sess._socket.readsentmsg();
-        self.assertEqual(sent, b'\x00\x00\x00o\x00`Skan\x04from(\ttest_name\x03seq&\x012\x02to(\x02me\x08instance(\x0eother_instance\x05reply&\x014\x05group(\tour_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00\x8b\x00{{"from": "test_name", "seq": 2, "to": "me", "instance": "other_instance", "reply": 4, "group": "our_group", "type": "send"}{"hello": "a"}')
         
         sess.group_reply({ 'from': 'me', 'group': 'our_group', 'instance': 'other_instance', 'seq': 9}, {"hello": "a"})
         sent = sess._socket.readsentmsg();
-        self.assertEqual(sent, b'\x00\x00\x00o\x00`Skan\x04from(\ttest_name\x03seq&\x013\x02to(\x02me\x08instance(\x0eother_instance\x05reply&\x019\x05group(\tour_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00\x8b\x00{{"from": "test_name", "seq": 3, "to": "me", "instance": "other_instance", "reply": 9, "group": "our_group", "type": "send"}{"hello": "a"}')
         
         
 if __name__ == "__main__":

+ 0 - 109
src/lib/python/isc/cc/tests/test.py

@@ -1,109 +0,0 @@
-# -*- coding: UTF8 -*-
-
-import isc
-import unittest
-
-class TestCCWireEncoding(unittest.TestCase):
-    def setUp(self): pass
-
-    def test_to_wire_of_string(self):
-        wire = isc.cc.message.to_wire({ "simple" : "string" })
-        self.assertEqual(wire, b'Skan\x06simple(\x06string')
-
-    def test_from_wire_of_string(self):
-        wire = b'Skan\x06simple(\x06string'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], "string")
-
-    def test_to_wire_of_binary_string(self):
-        wire = isc.cc.message.to_wire({ "simple" : b'\x01\xff\x02\x85' })
-        self.assertEqual(wire, b'Skan\x06simple!\x04\x01\xff\x02\x85')
-
-    def test_from_wire_of_binary_string(self):
-        wire = b'Skan\x06simple!\x04\x01\xff\x02\x85'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], b'\x01\xff\x02\x85')
-
-    def test_to_wire_of_list(self):
-        wire = isc.cc.message.to_wire({ "simple" : [ "string" ] })
-        self.assertEqual(wire, b'Skan\x06simple#\x08(\x06string')
-
-    def test_from_wire_of_list(self):
-        wire = b'Skan\x06simple#\x08(\x06string'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], [ "string" ])
-
-    def test_to_wire_of_hash(self):
-        wire = isc.cc.message.to_wire({ "simple" : { "string" : 1 }})
-        self.assertEqual(wire, b'Skan\x06simple"\n\x06string&\x011')
-
-    def test_from_wire_of_hash(self):
-        wire = b'Skan\x06simple"\n\x06string(\x011'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], { "string" : '1' })
-
-    def test_to_wire_of_none(self):
-        wire = isc.cc.message.to_wire({ "simple" : None })
-        self.assertEqual(wire, b'Skan\x06simple\x04')
-
-    def test_from_wire_of_none(self):
-        wire = b'Skan\x06simple\x04'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], None)
-
-    def test_to_wire_of_empty_string(self):
-        wire = isc.cc.message.to_wire({ "simple" : "" })
-        self.assertEqual(wire, b'Skan\x06simple(\x00')
-
-    def test_from_wire_of_empty_string(self):
-        wire = b'Skan\x06simple(\x00'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], "")
-
-    def test_to_wire_of_utf8_string(self):
-        wire = isc.cc.message.to_wire({ "simple" : "せんせい" })
-        self.assertEqual(wire, b'Skan\x06simple(\x0c\xe3\x81\x9b\xe3\x82\x93\xe3\x81\x9b\xe3\x81\x84')
-
-    def test_from_wire_of_utf8_string(self):
-        wire = b'Skan\x06simple(\x0c\xe3\x81\x9b\xe3\x82\x93\xe3\x81\x9b\xe3\x81\x84'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["simple"], "せんせい")
-
-    def test_to_wire_of_utf8_label(self):
-        wire = isc.cc.message.to_wire({ "せんせい" : "string" })
-        self.assertEqual(wire, b'Skan\x0c\xe3\x81\x9b\xe3\x82\x93\xe3\x81\x9b\xe3\x81\x84(\x06string')
-
-    def test_from_wire_of_utf8_label(self):
-        wire = b'Skan\x0c\xe3\x81\x9b\xe3\x82\x93\xe3\x81\x9b\xe3\x81\x84(\x06string'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["せんせい"], "string")
-
-    def test_to_wire_of_bool_true(self):
-        wire = isc.cc.message.to_wire({ "bool": True })
-        self.assertEqual(wire, b'Skan\x04bool%\x011')
-
-    def test_to_wire_of_bool_false(self):
-        wire = isc.cc.message.to_wire({ "bool": False })
-        self.assertEqual(wire, b'Skan\x04bool%\x010')
-
-    def test_from_wire_of_bool_true(self):
-        wire = b'Skan\x04bool%\x01\x01'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["bool"], True)
-
-    def test_from_wire_of_bool_true(self):
-        wire = b'Skan\x04bool%\x01\x00'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["bool"], False)
-
-    def test_to_wire_of_int(self):
-        wire = isc.cc.message.to_wire({ "number": 123 })
-        self.assertEqual(wire, b'Skan\x06number&\x03123')
-
-    def test_from_wire_of_int(self):
-        wire = b'Skan\x06number&\x03123'
-        decoded = isc.cc.message.from_wire(wire)
-        self.assertEqual(decoded["number"], 123)
-    
-if __name__ == '__main__':
-    unittest.main()