data.cc 21.5 KB
Newer Older
Jelte Jansen's avatar
Jelte Jansen committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC 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.

// $Id$
16

17
#include <config.h>
18

19
#include <cc/data.h>
20

JINMEI Tatuya's avatar
JINMEI Tatuya committed
21
#include <cassert>
Jeremy C. Reed's avatar
Jeremy C. Reed committed
22
#include <climits>
23 24
#include <cstdio>
#include <iostream>
25
#include <string>
26 27
#include <sstream>

28
#include <boost/algorithm/string.hpp> // for iequals
29

Jelte Jansen's avatar
Jelte Jansen committed
30 31
#include <cmath>

32 33
using namespace std;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
34 35 36
namespace isc {
namespace data {

37
std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
38
Element::str() {
39 40
    std::stringstream ss;
    toJSON(ss);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
41
    return (ss.str());
42 43 44
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
45
Element::toWire() {
46 47
    std::stringstream ss;
    toJSON(ss);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
48
    return (ss.str());
49 50 51
}

void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
52
Element::toWire(std::ostream& ss) {
53 54 55
    toJSON(ss);
}

56 57 58 59 60 61 62 63
//
// The following methods are effectively empty, and their parameters are
// unused.  To silence compilers that warn unused function parameters,
// we specify a (compiler dependent) special keyword when available.
// It's defined in config.h, and to avoid including this header file from
// installed files we define the methods here.
//
bool
Jelte Jansen's avatar
Jelte Jansen committed
64
Element::getValue(long int& t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
65
    return (false);
66 67 68 69
}

bool
Element::getValue(double& t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
70
    return (false);
71 72 73 74
}

bool
Element::getValue(bool& t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
75
    return (false);
76 77 78 79
}

bool
Element::getValue(std::string& t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
80
    return (false);
81 82 83 84
}

bool
Element::getValue(std::vector<ElementPtr>& t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
85
    return (false);
86 87 88 89
}

bool
Element::getValue(std::map<std::string, ElementPtr>& t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
90
    return (false);
91 92 93
}

bool
Jelte Jansen's avatar
Jelte Jansen committed
94
Element::setValue(const long int v UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
95
    return (false);
96 97 98 99
}

bool
Element::setValue(const double v UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
100
    return (false);
101 102 103 104
}

bool
Element::setValue(const bool t UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
105
    return (false);
106 107 108 109
}

bool
Element::setValue(const std::string& v UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
110
    return (false);
111 112 113 114
}

bool
Element::setValue(const std::vector<ElementPtr>& v UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
115
    return (false);
116 117 118
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
119 120
Element::setValue(const std::map<std::string, ElementPtr>& v UNUSED_PARAM) {
    return (false);
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
}

ElementPtr
Element::get(const int i UNUSED_PARAM) {
    isc_throw(TypeError, "get(int) called on a non-list Element");
}

void
Element::set(const size_t i UNUSED_PARAM, ElementPtr element UNUSED_PARAM) {
    isc_throw(TypeError, "set(int, element) called on a non-list Element");
}

void
Element::add(ElementPtr element UNUSED_PARAM) {
    isc_throw(TypeError, "add() called on a non-list Element");
}

void
Element::remove(const int i UNUSED_PARAM) {
    isc_throw(TypeError, "remove(int) called on a non-list Element");
}

size_t
Element::size() {
    isc_throw(TypeError, "size() called on a non-list Element");
}

ElementPtr
Element::get(const std::string& name UNUSED_PARAM) {
    isc_throw(TypeError, "get(string) called on a non-map Element");
}

void
Element::set(const std::string& name UNUSED_PARAM,
             ElementPtr element UNUSED_PARAM)
{
    isc_throw(TypeError, "set(name, element) called on a non-map Element");
}

void
Element::remove(const std::string& name UNUSED_PARAM) {
    isc_throw(TypeError, "remove(string) called on a non-map Element");
}

bool
Element::contains(const std::string& name UNUSED_PARAM) {
    isc_throw(TypeError, "contains(string) called on a non-map Element");
}

ElementPtr
Element::find(const std::string& identifier UNUSED_PARAM) {
    isc_throw(TypeError, "find(string) called on a non-map Element");
}

bool
Element::find(const std::string& identifier UNUSED_PARAM,
              ElementPtr& t UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
179
    return (false);
180 181
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
182 183
namespace {
inline void
Jelte Jansen's avatar
Jelte Jansen committed
184
throwJSONError(const std::string& error, const std::string& file, int line, int pos)
Jelte Jansen's avatar
Jelte Jansen committed
185
{
Jelte Jansen's avatar
Jelte Jansen committed
186 187 188
    std::stringstream ss;
    ss << error << " in " + file + ":" << line << ":" << pos;
    isc_throw(JSONError, ss.str());
189
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
190
}
191

Jelte Jansen's avatar
Jelte Jansen committed
192
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
193
    return (out << e->str());
194 195
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
196
bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
197
    return (a->equals(b));
198 199
};

200 201 202
//
// factory functions
//
203 204
ElementPtr
Element::create() {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
205
    return (ElementPtr(new NullElement()));
206 207
}

208
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
209
Element::create(const long int i) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
210
    return (ElementPtr(new IntElement(i)));
211 212 213
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
214
Element::create(const double d) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
215
    return (ElementPtr(new DoubleElement(d)));
216 217 218
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
219
Element::create(const std::string& s) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
220
    return (ElementPtr(new StringElement(s)));
221 222 223
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
224
Element::create(const bool b) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
225
    return (ElementPtr(new BoolElement(b)));
226 227 228
}

ElementPtr
229
Element::createList() {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
230
    return (ElementPtr(new ListElement()));
231 232 233
}

ElementPtr
234
Element::createMap() {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
235
    return (ElementPtr(new MapElement()));
236 237 238 239
}


//
240
// helper functions for fromJSON factory
241
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
242 243
namespace {
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
244 245
char_in(const char c, const char *chars) {
    for (size_t i = 0; i < strlen(chars); ++i) {
246
        if (chars[i] == c) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
247
            return (true);
248 249
        }
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
250
    return (false);
251 252
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
253
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
254
skip_chars(std::istream &in, const char *chars, int& line, int& pos) {
255 256
    char c = in.peek();
    while (char_in(c, chars) && c != EOF) {
257
        if (c == '\n') {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
258
            ++line;
259 260
            pos = 1;
        } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
261
            ++pos;
262
        }
263 264 265 266 267 268 269
        in.get();
        c = in.peek();
    }
}

// skip on the input stream to one of the characters in chars
// if another character is found this function returns false
270
// unless that character is specified in the optional may_skip
271 272
//
// the character found is left on the stream
JINMEI Tatuya's avatar
JINMEI Tatuya committed
273
void
Jelte Jansen's avatar
Jelte Jansen committed
274 275
skip_to(std::istream &in, const std::string& file, int& line,
        int& pos, const char* chars, const char* may_skip="")
276 277
{
    char c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
278
    ++pos;
279
    while (c != EOF) {
280 281
        if (c == '\n') {
            pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
282
            ++line;
283
        }
284 285
        if (char_in(c, may_skip)) {
            c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
286
            ++pos;
287 288
        } else if (char_in(c, chars)) {
            while(char_in(in.peek(), may_skip)) {
289 290
                if (in.peek() == '\n') {
                    pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
291
                    ++line;
292
                }
293
                in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
294
                ++pos;
295 296
            }
            in.putback(c);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
297
            --pos;
298
            return;
299
        } else {
Jelte Jansen's avatar
Jelte Jansen committed
300
            throwJSONError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
301 302
        }
    }
Jelte Jansen's avatar
Jelte Jansen committed
303
    throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
304 305
}

306 307
// TODO: Should we check for all other official escapes here (and
// error on the rest)?
JINMEI Tatuya's avatar
JINMEI Tatuya committed
308
std::string
309
str_from_stringstream(std::istream &in, const std::string& file, const int line,
Jelte Jansen's avatar
Jelte Jansen committed
310
                      int& pos) throw (JSONError)
311 312 313 314
{
    char c = 0;
    std::stringstream ss;
    c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
315
    ++pos;
316 317
    if (c == '"') {
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
318
        ++pos;
319
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
320
        throwJSONError("String expected", file, line, pos);
321 322 323 324 325
    }
    while (c != EOF && c != '"') {
        ss << c;
        if (c == '\\' && in.peek() == '"') {
            ss << in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
326
            ++pos;
327 328
        }
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
329
        ++pos;
330
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
331
    return (ss.str());
332 333
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
334
std::string
335
word_from_stringstream(std::istream &in, int& pos) {
336 337 338 339
    std::stringstream ss;
    while (isalpha(in.peek())) {
        ss << (char) in.get();
    }
340
    pos += ss.str().size();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
341
    return (ss.str());
342 343
}

Jelte Jansen's avatar
Jelte Jansen committed
344 345 346 347 348 349
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();
350
    }
Jelte Jansen's avatar
Jelte Jansen committed
351
    pos += ss.str().size();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
352
    return (ss.str());
353 354
}

Jelte Jansen's avatar
Jelte Jansen committed
355 356 357
// 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)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
358
ElementPtr
359
from_stringstream_number(std::istream &in, int &pos) {
Jelte Jansen's avatar
Jelte Jansen committed
360
    long int i = 0;
361 362
    double d = 0.0;
    bool is_double = false;
Jelte Jansen's avatar
Jelte Jansen committed
363
    char *endptr;
364

Jelte Jansen's avatar
Jelte Jansen committed
365 366 367 368 369
    std::string number = number_from_stringstream(in, pos);

    i = strtol(number.c_str(), &endptr, 10);
    if (*endptr != '\0') {
        d = strtod(number.c_str(), &endptr);
370
        is_double = true;
Jelte Jansen's avatar
Jelte Jansen committed
371 372
        if (*endptr != '\0') {
            isc_throw(JSONError, std::string("Bad number: ") + number);
373
        } else {
374
            if (d == HUGE_VAL || d == -HUGE_VAL) {
Jelte Jansen's avatar
Jelte Jansen committed
375
                isc_throw(JSONError, std::string("Number overflow: ") + number);
376 377
            }
        }
Jelte Jansen's avatar
Jelte Jansen committed
378 379 380 381
    } else {
        if (i == LONG_MAX || i == LONG_MIN) {
            isc_throw(JSONError, std::string("Number overflow: ") + number);
        }
382
    }
Jelte Jansen's avatar
Jelte Jansen committed
383
    
384
    if (is_double) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
385
        return (Element::create(d));
386
    } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
387
        return (Element::create(i));
388 389 390
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
391
ElementPtr
392 393
from_stringstream_bool(std::istream &in, const std::string& file,
                       const int line, int& pos)
394
{
395
    const std::string word = word_from_stringstream(in, pos);
396
    if (boost::iequals(word, "True")) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
397
        return (Element::create(true));
398
    } else if (boost::iequals(word, "False")) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
399
        return (Element::create(false));
400
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
401
        throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
Jelte Jansen's avatar
Jelte Jansen committed
402
        // above is a throw shortcurt, return empty is never reached
JINMEI Tatuya's avatar
JINMEI Tatuya committed
403
        return (ElementPtr());
404 405 406
    }
}

407 408 409 410 411 412
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")) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
413
        return (Element::create());
414
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
415
        throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
416
        return (ElementPtr());
417 418 419
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
420
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
421
from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
422
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
423
    return (Element::create(str_from_stringstream(in, file, line, pos)));
424 425
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
426
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
427
from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
428 429
{
    char c = 0;
430
    ElementPtr list = Element::createList();
431 432
    ElementPtr cur_list_element;

433
    skip_chars(in, " \t\n", line, pos);
434
    while (c != EOF && c != ']') {
435
        if (in.peek() != ']') {
436
            cur_list_element = Element::fromJSON(in, file, line, pos);
437
            list->add(cur_list_element);
Jelte Jansen's avatar
Jelte Jansen committed
438
            skip_to(in, file, line, pos, ",]", " \t\n");
439 440
        }
        c = in.get();
441
        pos++;
442
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
443
    return (list);
444 445
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
446
ElementPtr
447 448
from_stringstream_map(std::istream &in, const std::string& file, int& line,
                      int& pos)
449
{
450
    ElementPtr map = Element::createMap();
451
    skip_chars(in, " \t\n", line, pos);
452
    char c = in.peek();
453 454
    if (c == '}') {
        // empty map, skip closing curly
455
        c = in.get();
456 457
    } else {
        while (c != EOF && c != '}') {
458
            std::string key = str_from_stringstream(in, file, line, pos);
459

460 461 462 463
            skip_to(in, file, line, pos, ":", " \t\n");
            // skip the :
            in.get();
            pos++;
464

465
            ElementPtr value = Element::fromJSON(in, file, line, pos);
466 467
            map->set(key, value);
            
468 469 470 471
            skip_to(in, file, line, pos, ",}", " \t\n");
            c = in.get();
            pos++;
        }
472
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
473
    return (map);
474
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
475
}
476

477 478 479
std::string
Element::typeToName(Element::types type)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
480
    switch (type) {
481
    case Element::integer:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
482
        return (std::string("integer"));
483
    case Element::real:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
484
        return (std::string("real"));
485
    case Element::boolean:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
486
        return (std::string("boolean"));
487
    case Element::string:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
488
        return (std::string("string"));
489
    case Element::list:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
490
        return (std::string("list"));
491
    case Element::map:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
492
        return (std::string("map"));
493
    case Element::null:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
494
        return (std::string("null"));
495
    case Element::any:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
496
        return (std::string("any"));
497
    default:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
498
        return (std::string("unknown"));
499 500 501 502 503 504
    }
}

Element::types
Element::nameToType(const std::string& type_name) {
    if (type_name == "integer") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
505
        return (Element::integer);
506
    } else if (type_name == "real") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
507
        return (Element::real);
508
    } else if (type_name == "boolean") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
509
        return (Element::boolean);
510
    } else if (type_name == "string") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
511
        return (Element::string);
512
    } else if (type_name == "list") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
513
        return (Element::list);
514
    } else if (type_name == "map") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
515
        return (Element::map);
516
    } else if (type_name == "null") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
517
        return (Element::null);
518
    } else if (type_name == "any") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
519
        return (Element::any);
520 521 522 523 524
    } else {
        isc_throw(TypeError, type_name + " is not a valid type name");
    }
}

525
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
526
Element::fromJSON(std::istream& in) throw(JSONError) {
527
    int line = 1, pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
528
    return (fromJSON(in, "<istream>", line, pos));
529 530 531
}

ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
532
Element::fromJSON(std::istream& in, const std::string& file_name) throw(JSONError)
533 534
{
    int line = 1, pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
535
    return (fromJSON(in, file_name, line, pos));
536 537 538
}

ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
539
Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos) throw(JSONError)
540 541 542 543
{
    char c = 0;
    ElementPtr element;
    bool el_read = false;
544
    skip_chars(in, " \n\t", line, pos);
545 546
    while (c != EOF && !el_read) {
        c = in.get();
547
        pos++;
548 549 550 551 552 553 554 555 556 557 558
        switch(c) {
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
559 560
            case '-':
            case '+':
Jelte Jansen's avatar
Jelte Jansen committed
561 562
            case '.':
                in.putback(c);
563 564 565
                element = from_stringstream_number(in, pos);
                el_read = true;
                break;
566 567 568 569 570
            case 't':
            case 'T':
            case 'f':
            case 'F':
                in.putback(c);
Jelte Jansen's avatar
Jelte Jansen committed
571
                element = from_stringstream_bool(in, file, line, pos);
572 573
                el_read = true;
                break;
574
            case 'n':
575
            case 'N':
576 577 578 579
                in.putback(c);
                element = from_stringstream_null(in, file, line, pos);
                el_read = true;
                break;
580 581
            case '"':
                in.putback('"');
Jelte Jansen's avatar
Jelte Jansen committed
582
                element = from_stringstream_string(in, file, line, pos);
583 584 585
                el_read = true;
                break;
            case '[':
Jelte Jansen's avatar
Jelte Jansen committed
586
                element = from_stringstream_list(in, file, line, pos);
587 588 589
                el_read = true;
                break;
            case '{':
Jelte Jansen's avatar
Jelte Jansen committed
590
                element = from_stringstream_map(in, file, line, pos);
591 592
                el_read = true;
                break;
593 594
            case EOF:
                break;
595
            default:
Jelte Jansen's avatar
Jelte Jansen committed
596
                throwJSONError(std::string("error: unexpected character ") + c, file, line, pos);
597 598 599 600
                break;
        }
    }
    if (el_read) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
601
        return (element);
602
    } else {
603
        isc_throw(JSONError, "nothing read");
604 605 606
    }
}

607
ElementPtr
608
Element::fromJSON(const std::string &in) {
609 610
    std::stringstream ss;
    ss << in;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
611
    return (fromJSON(ss, "<string>"));
612
}
613

614 615 616
// to JSON format

void
617
IntElement::toJSON(std::ostream& ss)
618
{
619
    ss << intValue();
620 621
}

622
void
623
DoubleElement::toJSON(std::ostream& ss)
624
{
625
    ss << doubleValue();
626 627
}

628
void
629
BoolElement::toJSON(std::ostream& ss)
630
{
631
    if (boolValue()) {
632
        ss << "true";
633
    } else {
634
        ss << "false";
635 636 637
    }
}

638
void
639
NullElement::toJSON(std::ostream& ss)
640 641 642 643 644
{
    ss << "null";
}

void
645
StringElement::toJSON(std::ostream& ss)
646
{
647
    ss << "\"";
648
    ss << stringValue();
649 650 651
    ss << "\"";
}

652
void
653
ListElement::toJSON(std::ostream& ss)
654
{
655
    ss << "[ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
656 657 658 659

    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin();
         it != v.end(); ++it) {
660 661 662
        if (it != v.begin()) {
            ss << ", ";
        }
663
        (*it)->toJSON(ss);
664 665 666 667
    }
    ss << " ]";
}

668
void
669
MapElement::toJSON(std::ostream& ss)
670 671
{
    ss << "{ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
672 673 674 675

    const std::map<std::string, ElementPtr>& m = mapValue();
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
         it != m.end(); ++it) {
676 677 678 679
        if (it != m.begin()) {
            ss << ", ";
        }
        ss << "\"" << (*it).first << "\": ";
680
        if ((*it).second) {
681
            (*it).second->toJSON(ss);
682 683 684
        } else {
            ss << "None";
        }
685
    }
686
    ss << " }";
687 688
}

689
// throws when one of the types in the path (except the one
690 691 692 693
// we're looking for) is not a MapElement
// returns 0 if it could simply not be found
// should that also be an exception?
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
694 695
MapElement::find(const std::string& id) {
    const size_t sep = id.find('/');
696
    if (sep == std::string::npos) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
697
        return (get(id));
698 699 700
    } else {
        ElementPtr ce = get(id.substr(0, sep));
        if (ce) {
701
            // ignore trailing slash
JINMEI Tatuya's avatar
JINMEI Tatuya committed
702
            if  (sep + 1 != id.size()) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
703
                return (ce->find(id.substr(sep + 1)));
704
            } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
705
                return (ce);
706
            }
707
        } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
708
            return (ElementPtr());
709 710 711 712 713
        }
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
714
Element::fromWire(const std::string& s) {
715 716
    std::stringstream ss;
    ss << s;
717
    int line = 0, pos = 0;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
718
    return (fromJSON(ss, "<wire>", line, pos));
719 720 721
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
722
Element::fromWire(std::stringstream& in, int length) {
723 724 725
    //
    // Check protocol version
    //
726 727 728 729 730 731 732 733
    //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;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
734
    return (fromJSON(in, "<wire>", line, pos));
735 736
}

737 738
void
MapElement::set(const std::string& key, ElementPtr value) {
739
    m[key] = value;
740 741
}

742 743 744
bool
MapElement::find(const std::string& id, ElementPtr& t) {
    try {
Jelte Jansen's avatar
Jelte Jansen committed
745
        ElementPtr p = find(id);
746 747
        if (p) {
            t = p;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
748
            return (true);
749
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
750
    } catch (const TypeError& e) {
751 752
        // ignore
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
753
    return (false);
754
}
755

756
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
757
IntElement::equals(ElementPtr other) {
758 759 760 761 762
    return (other->getType() == Element::integer) &&
           (i == other->intValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
763
DoubleElement::equals(ElementPtr other) {
764 765 766 767 768
    return (other->getType() == Element::real) &&
           (d == other->doubleValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
769
BoolElement::equals(ElementPtr other) {
770 771 772 773
    return (other->getType() == Element::boolean) &&
           (b == other->boolValue());
}

774 775
bool
NullElement::equals(ElementPtr other) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
776
    return (other->getType() == Element::null);
777 778
}

779
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
780
StringElement::equals(ElementPtr other) {
781 782 783 784 785
    return (other->getType() == Element::string) &&
           (s == other->stringValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
786
ListElement::equals(ElementPtr other) {
787
    if (other->getType() == Element::list) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
788
        const int s = size();
789
        if (s != other->size()) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
790
            return (false);
791
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
792
        for (int i = 0; i < s; ++i) {
793
            if (!get(i)->equals(other->get(i))) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
794
                return (false);
795 796
            }
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
797
        return (true);
798
    } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
799
        return (false);
800 801 802 803
    }
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
804
MapElement::equals(ElementPtr other) {
805 806
    if (other->getType() == Element::map) {
        std::map<std::string, ElementPtr> m = mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
807
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
808 809 810
             it != m.end() ; ++it) {
            if (other->contains((*it).first)) {
                if (!get((*it).first)->equals(other->get((*it).first))) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
811
                    return (false);
812 813
                }
            } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
814
                return (false);
815 816 817 818 819 820 821 822
            }
        }
        // quickly walk through the other map too, to see if there's
        // anything in there that we don't have. We don't need to
        // compare those elements; if one of them is missing we
        // differ (and if it's not missing the loop above has checked
        // it)
        m = other->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
823
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
824 825
             it != m.end() ; ++it) {
            if (!contains((*it).