data.cc 21.1 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 18
#include "config.h"

19 20
#include "data.h"

JINMEI Tatuya's avatar
JINMEI Tatuya committed
21
#include <cassert>
22 23
#include <cstdio>
#include <iostream>
24
#include <string>
25 26
#include <sstream>

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

using namespace std;

31
namespace {
32 33
const unsigned char PROTOCOL_VERSION[4] = { 0x53, 0x6b, 0x61, 0x6e };

34
const unsigned char ITEM_BLOB = 0x01;
35 36 37
const unsigned char ITEM_HASH = 0x02;
const unsigned char ITEM_LIST = 0x03;
const unsigned char ITEM_NULL = 0x04;
Jelte Jansen's avatar
Jelte Jansen committed
38
const unsigned char ITEM_BOOL = 0x05;
39
const unsigned char ITEM_INT  = 0x06;
40
const unsigned char ITEM_REAL = 0x07;
41
const unsigned char ITEM_UTF8 = 0x08;
42 43 44 45 46 47
const unsigned char ITEM_MASK = 0x0f;

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;
48
}
49

JINMEI Tatuya's avatar
JINMEI Tatuya committed
50 51 52
namespace isc {
namespace data {

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
std::string
Element::str()
{
    std::stringstream ss;
    toJSON(ss);
    return ss.str();
}

std::string
Element::toWire()
{
    std::stringstream ss;
    toJSON(ss);
    return ss.str();
}

void
Element::toWire(std::stringstream& ss)
{
    toJSON(ss);
}

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 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 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
//
// 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
Element::getValue(int& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(double& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(bool& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(std::string& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(std::vector<ElementPtr>& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(std::map<std::string, ElementPtr>& t UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const int v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const double v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const bool t UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const std::string& v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const std::vector<ElementPtr>& v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const std::map<std::string, ElementPtr>& v UNUSED_PARAM)
{
    return false;
}

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)
{
    return false;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
202 203
namespace {
inline void
204
throwParseError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
Jelte Jansen's avatar
Jelte Jansen committed
205 206 207
{
    if (line != 0 || pos != 0) {
        std::stringstream ss;
208
        ss << error << " in " + file + ":" << line << ":" << pos;
Jelte Jansen's avatar
Jelte Jansen committed
209 210 211 212
        throw ParseError(ss.str());
    } else {
        throw ParseError(error);
    }
213
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
214
}
215

Jelte Jansen's avatar
Jelte Jansen committed
216 217
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e) {
    return out << e->str();
218 219
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
220
bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b) {
221 222 223
    return a->equals(b);
};

224 225 226
//
// factory functions
//
227 228 229 230 231
ElementPtr
Element::create() {
    return ElementPtr(new NullElement());
}

232
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
233
Element::create(const int i) {
234
    return ElementPtr(new IntElement(i));
235 236 237
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
238
Element::create(const double d) {
239
    return ElementPtr(new DoubleElement(d));
240 241 242
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
243
Element::create(const std::string& s) {
244
    return ElementPtr(new StringElement(s));
245 246 247
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
248
Element::create(const bool b) {
249
    return ElementPtr(new BoolElement(b));
250 251 252
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
253
Element::create(const std::vector<ElementPtr>& v) {
254
    return ElementPtr(new ListElement(v));
255 256 257
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
258
Element::create(const std::map<std::string, ElementPtr>& m) {
259 260 261 262 263 264
    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");
        }
    }
265
    return ElementPtr(new MapElement(m));
266 267 268 269
}


//
270
// helper functions for createFromString factory
271
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
272 273
namespace {
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
274 275
char_in(const char c, const char *chars) {
    for (size_t i = 0; i < strlen(chars); ++i) {
276 277 278 279 280 281 282
        if (chars[i] == c) {
            return true;
        }
    }
    return false;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
283
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
284
skip_chars(std::istream &in, const char *chars, int& line, int& pos) {
285 286
    char c = in.peek();
    while (char_in(c, chars) && c != EOF) {
287
        if (c == '\n') {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
288
            ++line;
289 290
            pos = 1;
        } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
291
            ++pos;
292
        }
293 294 295 296 297 298 299
        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
300
// unless that character is specified in the optional may_skip
301 302
//
// the character found is left on the stream
JINMEI Tatuya's avatar
JINMEI Tatuya committed
303
void
Jelte Jansen's avatar
Jelte Jansen committed
304 305
skip_to(std::istream &in, const std::string& file, int& line,
        int& pos, const char* chars, const char* may_skip="")
306 307
{
    char c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
308
    ++pos;
309
    while (c != EOF) {
310 311
        if (c == '\n') {
            pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
312
            ++line;
313
        }
314 315
        if (char_in(c, may_skip)) {
            c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
316
            ++pos;
317 318
        } else if (char_in(c, chars)) {
            while(char_in(in.peek(), may_skip)) {
319 320
                if (in.peek() == '\n') {
                    pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
321
                    ++line;
322
                }
323
                in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
324
                ++pos;
325 326
            }
            in.putback(c);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
327
            --pos;
328
            return;
329
        } else {
Jelte Jansen's avatar
Jelte Jansen committed
330
            throwParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
331 332
        }
    }
Jelte Jansen's avatar
Jelte Jansen committed
333
    throwParseError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
334 335
}

336 337
// TODO: Should we check for all other official escapes here (and
// error on the rest)?
JINMEI Tatuya's avatar
JINMEI Tatuya committed
338
std::string
339 340
str_from_stringstream(std::istream &in, const std::string& file, const int line,
                      int& pos) throw (ParseError)
341 342 343 344
{
    char c = 0;
    std::stringstream ss;
    c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
345
    ++pos;
346 347
    if (c == '"') {
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
348
        ++pos;
349
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
350
        throwParseError("String expected", file, line, pos);
351 352 353 354 355
    }
    while (c != EOF && c != '"') {
        ss << c;
        if (c == '\\' && in.peek() == '"') {
            ss << in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
356
            ++pos;
357 358
        }
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
359
        ++pos;
360 361 362 363
    }
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
364
std::string
365
word_from_stringstream(std::istream &in, int& pos) {
366 367 368 369
    std::stringstream ss;
    while (isalpha(in.peek())) {
        ss << (char) in.get();
    }
370
    pos += ss.str().size();
371 372 373
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
374
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
375
count_chars_i(int i) {
376 377
    int result = 1;
    while (i > 10) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
378 379
        ++result;
        i = i / 10;
380 381 382 383
    }
    return result;
}

384
// TODO: range checks
JINMEI Tatuya's avatar
JINMEI Tatuya committed
385
ElementPtr
386 387 388 389 390
from_stringstream_number(std::istream &in, int &pos) {
    int i, d_i;
    double d = 0.0;
    bool is_double = false;

391
    in >> i;
392
    pos += count_chars_i(i);
393
    if (in.peek() == '.') {
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
        is_double = true;
        in.get();
        pos++;
        in >> d_i;
        d = i + (double)d_i / 10;
        pos += count_chars_i(d_i);
    }
    if (in.peek() == 'e' || in.peek() == 'E') {
        int e;
        in.get();
        pos++;
        in >> e;
        pos += count_chars_i(e);
        if (e == 0) {
            d = 1;
            i = 1;
        } else if (e < 0) {
            if (!is_double) {
                is_double = true;
                d = i;
            }
            while (e < 0) {
                d = d / 10;
                e++;
            }
        } else {
            if (is_double) {
                while (e > 0) {
                    d = d * 10;
                    e--;
                }
            } else {
                while (e > 0) {
                    i = i * 10;
                    e--;
                }
            }
        }
    }

    if (is_double) {
435 436 437 438 439 440
        return Element::create(d);
    } else {
        return Element::create(i);
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
441
ElementPtr
442 443
from_stringstream_bool(std::istream &in, const std::string& file,
                       const int line, int& pos)
444
{
445
    const std::string word = word_from_stringstream(in, pos);
446 447 448 449 450
    if (boost::iequals(word, "True")) {
        return Element::create(true);
    } else if (boost::iequals(word, "False")) {
        return Element::create(false);
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
451 452 453
        throwParseError(std::string("Bad boolean value: ") + word, file, line, pos);
        // above is a throw shortcur, return empty is never reached
        return ElementPtr();
454 455 456
    }
}

457 458 459 460 461 462 463 464 465 466 467 468 469
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 {
        throwParseError(std::string("Bad null value: ") + word, file, line, pos);
        return ElementPtr();
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
470
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
471
from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
472
{
Jelte Jansen's avatar
Jelte Jansen committed
473
    return Element::create(str_from_stringstream(in, file, line, pos));
474 475
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
476
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
477
from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
478 479 480 481 482
{
    char c = 0;
    std::vector<ElementPtr> v;
    ElementPtr cur_list_element;

483
    skip_chars(in, " \t\n", line, pos);
484
    while (c != EOF && c != ']') {
485
        if (in.peek() != ']') {
Jelte Jansen's avatar
Jelte Jansen committed
486
            cur_list_element = Element::createFromString(in, file, line, pos);
487
            v.push_back(cur_list_element);
Jelte Jansen's avatar
Jelte Jansen committed
488
            skip_to(in, file, line, pos, ",]", " \t\n");
489 490
        }
        c = in.get();
491
        pos++;
492 493 494 495
    }
    return Element::create(v);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
496
ElementPtr
497 498
from_stringstream_map(std::istream &in, const std::string& file, int& line,
                      int& pos)
499 500
{
    std::map<std::string, ElementPtr> m;
501
    skip_chars(in, " \t\n", line, pos);
502
    char c = in.peek();
503 504
    if (c == '}') {
        // empty map, skip closing curly
505
        c = in.get();
506 507
    } else {
        while (c != EOF && c != '}') {
508 509
            std::pair<std::string, ElementPtr> p;

510
            p.first = str_from_stringstream(in, file, line, pos);
511 512 513 514 515 516
            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);
            }

517 518 519 520 521 522 523 524 525 526
            skip_to(in, file, line, pos, ":", " \t\n");
            // skip the :
            in.get();
            pos++;
            p.second = Element::createFromString(in, file, line, pos);
            m.insert(p);
            skip_to(in, file, line, pos, ",}", " \t\n");
            c = in.get();
            pos++;
        }
527 528 529
    }
    return Element::create(m);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
530
}
531 532

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
533
Element::createFromString(std::istream& in) throw(ParseError) {
534
    int line = 1, pos = 1;
535 536 537 538
    return createFromString(in, "<istream>", line, pos);
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
539
Element::createFromString(std::istream& in, const std::string& file_name) throw(ParseError)
540 541 542
{
    int line = 1, pos = 1;
    return createFromString(in, file_name, line, pos);
543 544 545
}

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

610
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
611
Element::createFromString(const std::string &in) {
612 613
    std::stringstream ss;
    ss << in;
614
    return createFromString(ss, "<string>");
615
}
616

617 618 619 620 621
// to JSON format

void
IntElement::toJSON(std::stringstream& ss)
{
622
    ss << intValue();
623 624
}

625 626 627
void
DoubleElement::toJSON(std::stringstream& ss)
{
628
    ss << doubleValue();
629 630
}

631 632 633
void
BoolElement::toJSON(std::stringstream& ss)
{
634
    if (b) {
635
        ss << "true";
636
    } else {
637
        ss << "false";
638 639 640
    }
}

641 642 643 644 645 646 647 648 649
void
NullElement::toJSON(std::stringstream& ss)
{
    ss << "null";
}

void
StringElement::toJSON(std::stringstream& ss)
{
650
    ss << "\"";
651
    ss << stringValue();
652 653 654
    ss << "\"";
}

655 656 657
void
ListElement::toJSON(std::stringstream& ss)
{
658
    ss << "[ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
659 660 661 662

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

671 672 673 674
void
MapElement::toJSON(std::stringstream& ss)
{
    ss << "{ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
675 676 677 678

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

692
// throws when one of the types in the path (except the one
693 694 695 696
// 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
697 698
MapElement::find(const std::string& id) {
    const size_t sep = id.find('/');
699 700 701 702 703
    if (sep == std::string::npos) {
        return get(id);
    } else {
        ElementPtr ce = get(id.substr(0, sep));
        if (ce) {
704
            // ignore trailing slash
JINMEI Tatuya's avatar
JINMEI Tatuya committed
705 706
            if  (sep + 1 != id.size()) {
                return ce->find(id.substr(sep + 1));
707 708 709
            } else {
                return ce;
            }
710 711 712 713 714 715 716
        } else {
            return ElementPtr();
        }
    }
}

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

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

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

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

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

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

772 773 774 775 776
bool
NullElement::equals(ElementPtr other) {
    return other->getType() == Element::null;
}

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

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

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
802
MapElement::equals(ElementPtr other) {
803 804
    if (other->getType() == Element::map) {
        std::map<std::string, ElementPtr> m = mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
805
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
             it != m.end() ; ++it) {
            if (other->contains((*it).first)) {
                if (!get((*it).first)->equals(other->get((*it).first))) {
                    return false;
                }
            } else {
                return false;
            }
        }
        // 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
821
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
822 823 824 825 826 827 828 829 830 831 832
             it != m.end() ; ++it) {
            if (!contains((*it).first)) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

833
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
834
isNull(ElementPtr p) {
835 836 837
    return !p;
}

838
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
839
removeIdentical(ElementPtr a, const ElementPtr b) {
840 841 842
    if (!b) {
        return;
    }
843
    if (a->getType() != Element::map || b->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
844
        isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
845
    }
846

847
    std::map<std::string, ElementPtr> m = a->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
848
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
849 850 851 852 853 854 855
         it != m.end() ; ++it) {
        if (b->contains((*it).first)) {
            if (a->get((*it).first)->equals(b->get((*it).first))) {
                a->remove((*it).first);
            }
        }
    }
856
}
857

858
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
859
merge(ElementPtr element, const ElementPtr other) {
860 861
    if (element->getType() != Element::map ||
        other->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
862
        isc_throw(TypeError, "merge arguments not MapElements");
863 864 865
    }
    
    std::map<std::string, ElementPtr> m = other->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
866
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
867 868 869 870 871 872 873
         it != m.end() ; ++it) {
        if ((*it).second) {
            element->set((*it).first, (*it).second);
        } else if (element->contains((*it).first)) {
            element->remove((*it).first);
        }
    }
874
}
875

JINMEI Tatuya's avatar
JINMEI Tatuya committed
876 877
}
}