Kea 1.5.0
data.cc
Go to the documentation of this file.
1// Copyright (C) 2010-2018 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cc/data.h>
10
11#include <cstring>
12#include <cassert>
13#include <climits>
14#include <list>
15#include <map>
16#include <cstdio>
17#include <iostream>
18#include <iomanip>
19#include <string>
20#include <sstream>
21#include <fstream>
22#include <cerrno>
23
24#include <boost/lexical_cast.hpp>
25
26#include <cmath>
27
28using namespace std;
29
30namespace {
31const char* const WHITESPACE = " \b\f\n\r\t";
32} // end anonymous namespace
33
34namespace isc {
35namespace data {
36
37std::string
39 std::ostringstream ss;
40 ss << file_ << ":" << line_ << ":" << pos_;
41 return (ss.str());
42}
43
44std::ostream&
45operator<<(std::ostream& out, const Element::Position& pos) {
46 out << pos.str();
47 return (out);
48}
49
50std::string
51Element::str() const {
52 std::stringstream ss;
53 toJSON(ss);
54 return (ss.str());
55}
56
57std::string
59 std::stringstream ss;
60 toJSON(ss);
61 return (ss.str());
62}
63
64void
65Element::toWire(std::ostream& ss) const {
66 toJSON(ss);
67}
68
69bool
70Element::getValue(int64_t&) const {
71 return (false);
72}
73
74bool
75Element::getValue(double&) const {
76 return (false);
77}
78
79bool
80Element::getValue(bool&) const {
81 return (false);
82}
83
84bool
85Element::getValue(std::string&) const {
86 return (false);
87}
88
89bool
90Element::getValue(std::vector<ElementPtr>&) const {
91 return (false);
92}
93
94bool
95Element::getValue(std::map<std::string, ConstElementPtr>&) const {
96 return (false);
97}
98
99bool
100Element::setValue(const long long int) {
101 return (false);
102}
103
104bool
105Element::setValue(const double) {
106 return (false);
107}
108
109bool
110Element::setValue(const bool) {
111 return (false);
112}
113
114bool
115Element::setValue(const std::string&) {
116 return (false);
117}
118
119bool
120Element::setValue(const std::vector<ElementPtr>&) {
121 return (false);
122}
123
124bool
125Element::setValue(const std::map<std::string, ConstElementPtr>&) {
126 return (false);
127}
128
130Element::get(const int) const {
131 throwTypeError("get(int) called on a non-list Element");
132}
133
135Element::getNonConst(const int) const {
136 throwTypeError("get(int) called on a non-list Element");
137}
138
139void
140Element::set(const size_t, ElementPtr) {
141 throwTypeError("set(int, element) called on a non-list Element");
142}
143
144void
146 throwTypeError("add() called on a non-list Element");
147}
148
149void
150Element::remove(const int) {
151 throwTypeError("remove(int) called on a non-list Element");
152}
153
154size_t
156 throwTypeError("size() called on a non-list Element");
157}
158
159bool
161 throwTypeError("empty() called on a non-list Element");
162}
163
165Element::get(const std::string&) const {
166 throwTypeError("get(string) called on a non-map Element");
167}
168
169void
170Element::set(const std::string&, ConstElementPtr) {
171 throwTypeError("set(name, element) called on a non-map Element");
172}
173
174void
175Element::remove(const std::string&) {
176 throwTypeError("remove(string) called on a non-map Element");
177}
178
179bool
180Element::contains(const std::string&) const {
181 throwTypeError("contains(string) called on a non-map Element");
182}
183
185Element::find(const std::string&) const {
186 throwTypeError("find(string) called on a non-map Element");
187}
188
189bool
190Element::find(const std::string&, ConstElementPtr&) const {
191 return (false);
192}
193
194namespace {
195inline void
196throwJSONError(const std::string& error, const std::string& file, int line,
197 int pos)
198{
199 std::stringstream ss;
200 ss << error << " in " + file + ":" << line << ":" << pos;
201 isc_throw(JSONError, ss.str());
202}
203}
204
205std::ostream&
206operator<<(std::ostream& out, const Element& e) {
207 return (out << e.str());
208}
209
210bool
211operator==(const Element& a, const Element& b) {
212 return (a.equals(b));
213}
214
215bool operator!=(const Element& a, const Element& b) {
216 return (!a.equals(b));
217};
218
219//
220// factory functions
221//
224 return (ElementPtr(new NullElement(pos)));
225}
226
228Element::create(const long long int i, const Position& pos) {
229 return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
230}
231
233Element::create(const int i, const Position& pos) {
234 return (create(static_cast<long long int>(i), pos));
235};
236
238Element::create(const long int i, const Position& pos) {
239 return (create(static_cast<long long int>(i), pos));
243Element::create(const double d, const Position& pos) {
244 return (ElementPtr(new DoubleElement(d, pos)));
245}
246
248Element::create(const bool b, const Position& pos) {
249 return (ElementPtr(new BoolElement(b, pos)));
250}
251
253Element::create(const std::string& s, const Position& pos) {
254 return (ElementPtr(new StringElement(s, pos)));
255}
258Element::create(const char *s, const Position& pos) {
259 return (create(std::string(s), pos));
264 return (ElementPtr(new ListElement(pos)));
265}
266
269 return (ElementPtr(new MapElement(pos)));
270}
271
272
273//
274// helper functions for fromJSON factory
275//
276namespace {
277bool
278charIn(const int c, const char* chars) {
279 const size_t chars_len = std::strlen(chars);
280 for (size_t i = 0; i < chars_len; ++i) {
281 if (chars[i] == c) {
282 return (true);
283 }
284 }
285 return (false);
286}
287
288void
289skipChars(std::istream& in, const char* chars, int& line, int& pos) {
290 int c = in.peek();
291 while (charIn(c, chars) && c != EOF) {
292 if (c == '\n') {
293 ++line;
294 pos = 1;
295 } else {
296 ++pos;
297 }
298 in.ignore();
299 c = in.peek();
300 }
301}
302
303// skip on the input stream to one of the characters in chars
304// if another character is found this function throws JSONError
305// unless that character is specified in the optional may_skip
306//
307// It returns the found character (as an int value).
308int
309skipTo(std::istream& in, const std::string& file, int& line,
310 int& pos, const char* chars, const char* may_skip="")
311{
312 int c = in.get();
313 ++pos;
314 while (c != EOF) {
315 if (c == '\n') {
316 pos = 1;
317 ++line;
318 }
319 if (charIn(c, may_skip)) {
320 c = in.get();
321 ++pos;
322 } else if (charIn(c, chars)) {
323 while (charIn(in.peek(), may_skip)) {
324 if (in.peek() == '\n') {
325 pos = 1;
326 ++line;
327 } else {
328 ++pos;
329 }
330 in.ignore();
331 }
332 return (c);
333 } else {
334 throwJSONError(std::string("'") + std::string(1, c) + "' read, one of \"" + chars + "\" expected", file, line, pos);
335 }
336 }
337 throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
338 return (c); // shouldn't reach here, but some compilers require it
339}
340
341// TODO: Should we check for all other official escapes here (and
342// error on the rest)?
343std::string
344strFromStringstream(std::istream& in, const std::string& file,
345 const int line, int& pos)
346{
347 std::stringstream ss;
348 int c = in.get();
349 ++pos;
350 if (c == '"') {
351 c = in.get();
352 ++pos;
353 } else {
354 throwJSONError("String expected", file, line, pos);
355 }
356
357 while (c != EOF && c != '"') {
358 if (c == '\\') {
359 // see the spec for allowed escape characters
360 int d;
361 switch (in.peek()) {
362 case '"':
363 c = '"';
364 break;
365 case '/':
366 c = '/';
367 break;
368 case '\\':
369 c = '\\';
370 break;
371 case 'b':
372 c = '\b';
373 break;
374 case 'f':
375 c = '\f';
376 break;
377 case 'n':
378 c = '\n';
379 break;
380 case 'r':
381 c = '\r';
382 break;
383 case 't':
384 c = '\t';
385 break;
386 case 'u':
387 // skip first 0
388 in.ignore();
389 ++pos;
390 c = in.peek();
391 if (c != '0') {
392 throwJSONError("Unsupported unicode escape", file, line, pos);
393 }
394 // skip second 0
395 in.ignore();
396 ++pos;
397 c = in.peek();
398 if (c != '0') {
399 throwJSONError("Unsupported unicode escape", file, line, pos - 2);
400 }
401 // get first digit
402 in.ignore();
403 ++pos;
404 d = in.peek();
405 if ((d >= '0') && (d <= '9')) {
406 c = (d - '0') << 4;
407 } else if ((d >= 'A') && (d <= 'F')) {
408 c = (d - 'A' + 10) << 4;
409 } else if ((d >= 'a') && (d <= 'f')) {
410 c = (d - 'a' + 10) << 4;
411 } else {
412 throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 3);
413 }
414 // get second digit
415 in.ignore();
416 ++pos;
417 d = in.peek();
418 if ((d >= '0') && (d <= '9')) {
419 c |= d - '0';
420 } else if ((d >= 'A') && (d <= 'F')) {
421 c |= d - 'A' + 10;
422 } else if ((d >= 'a') && (d <= 'f')) {
423 c |= d - 'a' + 10;
424 } else {
425 throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 4);
426 }
427 break;
428 default:
429 throwJSONError("Bad escape", file, line, pos);
430 }
431 // drop the escaped char
432 in.ignore();
433 ++pos;
434 }
435 ss.put(c);
436 c = in.get();
437 ++pos;
438 }
439 if (c == EOF) {
440 throwJSONError("Unterminated string", file, line, pos);
441 }
442 return (ss.str());
443}
444
445std::string
446wordFromStringstream(std::istream& in, int& pos) {
447 std::stringstream ss;
448 while (isalpha(in.peek())) {
449 ss << (char) in.get();
450 }
451 pos += ss.str().size();
452 return (ss.str());
453}
454
455std::string
456numberFromStringstream(std::istream& in, int& pos) {
457 std::stringstream ss;
458 while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
459 in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
460 ss << (char) in.get();
461 }
462 pos += ss.str().size();
463 return (ss.str());
464}
465
466// Should we change from IntElement and DoubleElement to NumberElement
467// that can also hold an e value? (and have specific getters if the
468// value is larger than an int can handle)
469//
471fromStringstreamNumber(std::istream& in, const std::string& file,
472 const int& line, int& pos) {
473 // Remember position where the value starts. It will be set in the
474 // Position structure of the Element to be created.
475 const uint32_t start_pos = pos;
476 // This will move the pos to the end of the value.
477 const std::string number = numberFromStringstream(in, pos);
478
479 if (number.find_first_of(".eE") < number.size()) {
480 try {
481 return (Element::create(boost::lexical_cast<double>(number),
482 Element::Position(file, line, start_pos)));
483 } catch (const boost::bad_lexical_cast&) {
484 throwJSONError(std::string("Number overflow: ") + number,
485 file, line, start_pos);
486 }
487 } else {
488 try {
489 return (Element::create(boost::lexical_cast<int64_t>(number),
490 Element::Position(file, line, start_pos)));
491 } catch (const boost::bad_lexical_cast&) {
492 throwJSONError(std::string("Number overflow: ") + number, file,
493 line, start_pos);
494 }
495 }
496 return (ElementPtr());
497}
498
500fromStringstreamBool(std::istream& in, const std::string& file,
501 const int line, int& pos)
502{
503 // Remember position where the value starts. It will be set in the
504 // Position structure of the Element to be created.
505 const uint32_t start_pos = pos;
506 // This will move the pos to the end of the value.
507 const std::string word = wordFromStringstream(in, pos);
508
509 if (word == "true") {
510 return (Element::create(true, Element::Position(file, line,
511 start_pos)));
512 } else if (word == "false") {
513 return (Element::create(false, Element::Position(file, line,
514 start_pos)));
515 } else {
516 throwJSONError(std::string("Bad boolean value: ") + word, file,
517 line, start_pos);
518 }
519 return (ElementPtr());
520}
521
523fromStringstreamNull(std::istream& in, const std::string& file,
524 const int line, int& pos)
525{
526 // Remember position where the value starts. It will be set in the
527 // Position structure of the Element to be created.
528 const uint32_t start_pos = pos;
529 // This will move the pos to the end of the value.
530 const std::string word = wordFromStringstream(in, pos);
531 if (word == "null") {
532 return (Element::create(Element::Position(file, line, start_pos)));
533 } else {
534 throwJSONError(std::string("Bad null value: ") + word, file,
535 line, start_pos);
536 return (ElementPtr());
537 }
538}
539
541fromStringstreamString(std::istream& in, const std::string& file, int& line,
542 int& pos)
543{
544 // Remember position where the value starts. It will be set in the
545 // Position structure of the Element to be created.
546 const uint32_t start_pos = pos;
547 // This will move the pos to the end of the value.
548 const std::string string_value = strFromStringstream(in, file, line, pos);
549 return (Element::create(string_value, Element::Position(file, line,
550 start_pos)));
551}
552
554fromStringstreamList(std::istream& in, const std::string& file, int& line,
555 int& pos)
556{
557 int c = 0;
558 ElementPtr list = Element::createList(Element::Position(file, line, pos));
559 ElementPtr cur_list_element;
560
561 skipChars(in, WHITESPACE, line, pos);
562 while (c != EOF && c != ']') {
563 if (in.peek() != ']') {
564 cur_list_element = Element::fromJSON(in, file, line, pos);
565 list->add(cur_list_element);
566 c = skipTo(in, file, line, pos, ",]", WHITESPACE);
567 } else {
568 c = in.get();
569 ++pos;
570 }
571 }
572 return (list);
573}
574
576fromStringstreamMap(std::istream& in, const std::string& file, int& line,
577 int& pos)
578{
579 ElementPtr map = Element::createMap(Element::Position(file, line, pos));
580 skipChars(in, WHITESPACE, line, pos);
581 int c = in.peek();
582 if (c == EOF) {
583 throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
584 } else if (c == '}') {
585 // empty map, skip closing curly
586 in.ignore();
587 } else {
588 while (c != EOF && c != '}') {
589 std::string key = strFromStringstream(in, file, line, pos);
590
591 skipTo(in, file, line, pos, ":", WHITESPACE);
592 // skip the :
593
594 ConstElementPtr value = Element::fromJSON(in, file, line, pos);
595 map->set(key, value);
596
597 c = skipTo(in, file, line, pos, ",}", WHITESPACE);
598 }
599 }
600 return (map);
601}
602} // unnamed namespace
603
604std::string
606 switch (type) {
607 case Element::integer:
608 return (std::string("integer"));
609 case Element::real:
610 return (std::string("real"));
611 case Element::boolean:
612 return (std::string("boolean"));
613 case Element::string:
614 return (std::string("string"));
615 case Element::list:
616 return (std::string("list"));
617 case Element::map:
618 return (std::string("map"));
619 case Element::null:
620 return (std::string("null"));
621 case Element::any:
622 return (std::string("any"));
623 default:
624 return (std::string("unknown"));
625 }
626}
627
629Element::nameToType(const std::string& type_name) {
630 if (type_name == "integer") {
631 return (Element::integer);
632 } else if (type_name == "real") {
633 return (Element::real);
634 } else if (type_name == "boolean") {
635 return (Element::boolean);
636 } else if (type_name == "string") {
637 return (Element::string);
638 } else if (type_name == "list") {
639 return (Element::list);
640 } else if (type_name == "map") {
641 return (Element::map);
642 } else if (type_name == "named_set") {
643 return (Element::map);
644 } else if (type_name == "null") {
645 return (Element::null);
646 } else if (type_name == "any") {
647 return (Element::any);
648 } else {
649 isc_throw(TypeError, type_name + " is not a valid type name");
650 }
651}
652
654Element::fromJSON(std::istream& in, bool preproc) {
655
656 int line = 1, pos = 1;
657 stringstream filtered;
658 if (preproc) {
659 preprocess(in, filtered);
660 }
661
662 ElementPtr value = fromJSON(preproc ? filtered : in, "<istream>", line, pos);
663
664 return (value);
665}
666
668Element::fromJSON(std::istream& in, const std::string& file_name, bool preproc)
669{
670 int line = 1, pos = 1;
671 stringstream filtered;
672 if (preproc) {
673 preprocess(in, filtered);
674 }
675 return (fromJSON(preproc ? filtered : in, file_name, line, pos));
676}
677
679Element::fromJSON(std::istream& in, const std::string& file, int& line,
680 int& pos)
681{
682 int c = 0;
683 ElementPtr element;
684 bool el_read = false;
685 skipChars(in, WHITESPACE, line, pos);
686 while (c != EOF && !el_read) {
687 c = in.get();
688 pos++;
689 switch(c) {
690 case '1':
691 case '2':
692 case '3':
693 case '4':
694 case '5':
695 case '6':
696 case '7':
697 case '8':
698 case '9':
699 case '0':
700 case '-':
701 case '+':
702 case '.':
703 in.putback(c);
704 --pos;
705 element = fromStringstreamNumber(in, file, line, pos);
706 el_read = true;
707 break;
708 case 't':
709 case 'f':
710 in.putback(c);
711 --pos;
712 element = fromStringstreamBool(in, file, line, pos);
713 el_read = true;
714 break;
715 case 'n':
716 in.putback(c);
717 --pos;
718 element = fromStringstreamNull(in, file, line, pos);
719 el_read = true;
720 break;
721 case '"':
722 in.putback('"');
723 --pos;
724 element = fromStringstreamString(in, file, line, pos);
725 el_read = true;
726 break;
727 case '[':
728 element = fromStringstreamList(in, file, line, pos);
729 el_read = true;
730 break;
731 case '{':
732 element = fromStringstreamMap(in, file, line, pos);
733 el_read = true;
734 break;
735 case EOF:
736 break;
737 default:
738 throwJSONError(std::string("error: unexpected character ") + std::string(1, c), file, line, pos);
739 break;
740 }
741 }
742 if (el_read) {
743 return (element);
744 } else {
745 isc_throw(JSONError, "nothing read");
746 }
747}
748
750Element::fromJSON(const std::string& in, bool preproc) {
751 std::stringstream ss;
752 ss << in;
753
754 int line = 1, pos = 1;
755 stringstream filtered;
756 if (preproc) {
757 preprocess(ss, filtered);
758 }
759 ElementPtr result(fromJSON(preproc ? filtered : ss, "<string>", line, pos));
760 skipChars(ss, WHITESPACE, line, pos);
761 // ss must now be at end
762 if (ss.peek() != EOF) {
763 throwJSONError("Extra data", "<string>", line, pos);
764 }
765 return result;
766}
767
769Element::fromJSONFile(const std::string& file_name,
770 bool preproc) {
771 // zero out the errno to be safe
772 errno = 0;
773
774 std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
775 if (!infile.is_open())
776 {
777 const char* error = strerror(errno);
778 isc_throw(InvalidOperation, "failed to read file '" << file_name
779 << "': " << error);
780 }
781
782 return (fromJSON(infile, file_name, preproc));
783}
784
785// to JSON format
786
787void
788IntElement::toJSON(std::ostream& ss) const {
789 ss << intValue();
790}
791
792void
793DoubleElement::toJSON(std::ostream& ss) const {
794 ss << doubleValue();
795}
796
797void
798BoolElement::toJSON(std::ostream& ss) const {
799 if (boolValue()) {
800 ss << "true";
801 } else {
802 ss << "false";
803 }
804}
805
806void
807NullElement::toJSON(std::ostream& ss) const {
808 ss << "null";
809}
810
811void
812StringElement::toJSON(std::ostream& ss) const {
813 ss << "\"";
814 const std::string& str = stringValue();
815 for (size_t i = 0; i < str.size(); ++i) {
816 const char c = str[i];
817 // Escape characters as defined in JSON spec
818 // Note that we do not escape forward slash; this
819 // is allowed, but not mandatory.
820 switch (c) {
821 case '"':
822 ss << '\\' << c;
823 break;
824 case '\\':
825 ss << '\\' << c;
826 break;
827 case '\b':
828 ss << '\\' << 'b';
829 break;
830 case '\f':
831 ss << '\\' << 'f';
832 break;
833 case '\n':
834 ss << '\\' << 'n';
835 break;
836 case '\r':
837 ss << '\\' << 'r';
838 break;
839 case '\t':
840 ss << '\\' << 't';
841 break;
842 default:
843 if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
844 std::ostringstream esc;
845 esc << "\\u"
846 << hex
847 << setw(4)
848 << setfill('0')
849 << (static_cast<unsigned>(c) & 0xff);
850 ss << esc.str();
851 } else {
852 ss << c;
853 }
854 }
855 }
856 ss << "\"";
857}
858
859void
860ListElement::toJSON(std::ostream& ss) const {
861 ss << "[ ";
862
863 const std::vector<ElementPtr>& v = listValue();
864 for (std::vector<ElementPtr>::const_iterator it = v.begin();
865 it != v.end(); ++it) {
866 if (it != v.begin()) {
867 ss << ", ";
868 }
869 (*it)->toJSON(ss);
870 }
871 ss << " ]";
872}
873
874void
875MapElement::toJSON(std::ostream& ss) const {
876 ss << "{ ";
877
878 const std::map<std::string, ConstElementPtr>& m = mapValue();
879 for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
880 it != m.end(); ++it) {
881 if (it != m.begin()) {
882 ss << ", ";
883 }
884 ss << "\"" << (*it).first << "\": ";
885 if ((*it).second) {
886 (*it).second->toJSON(ss);
887 } else {
888 ss << "None";
889 }
890 }
891 ss << " }";
892}
893
894// throws when one of the types in the path (except the one
895// we're looking for) is not a MapElement
896// returns 0 if it could simply not be found
897// should that also be an exception?
899MapElement::find(const std::string& id) const {
900 const size_t sep = id.find('/');
901 if (sep == std::string::npos) {
902 return (get(id));
903 } else {
904 ConstElementPtr ce = get(id.substr(0, sep));
905 if (ce) {
906 // ignore trailing slash
907 if (sep + 1 != id.size()) {
908 return (ce->find(id.substr(sep + 1)));
909 } else {
910 return (ce);
911 }
912 } else {
913 return (ElementPtr());
914 }
915 }
916}
917
919Element::fromWire(const std::string& s) {
920 std::stringstream ss;
921 ss << s;
922 int line = 0, pos = 0;
923 return (fromJSON(ss, "<wire>", line, pos));
924}
925
927Element::fromWire(std::stringstream& in, int) {
928 //
929 // Check protocol version
930 //
931 //for (int i = 0 ; i < 4 ; ++i) {
932 // const unsigned char version_byte = get_byte(in);
933 // if (PROTOCOL_VERSION[i] != version_byte) {
934 // throw DecodeError("Protocol version incorrect");
935 // }
936 //}
937 //length -= 4;
938 int line = 0, pos = 0;
939 return (fromJSON(in, "<wire>", line, pos));
940}
941
942void
943MapElement::set(const std::string& key, ConstElementPtr value) {
944 m[key] = value;
945}
946
947bool
948MapElement::find(const std::string& id, ConstElementPtr& t) const {
949 try {
950 ConstElementPtr p = find(id);
951 if (p) {
952 t = p;
953 return (true);
954 }
955 } catch (const TypeError&) {
956 // ignore
957 }
958 return (false);
959}
960
961bool
962IntElement::equals(const Element& other) const {
963 return (other.getType() == Element::integer) &&
964 (i == other.intValue());
965}
966
967bool
968DoubleElement::equals(const Element& other) const {
969 return (other.getType() == Element::real) &&
970 (d == other.doubleValue());
971}
972
973bool
974BoolElement::equals(const Element& other) const {
975 return (other.getType() == Element::boolean) &&
976 (b == other.boolValue());
977}
978
979bool
980NullElement::equals(const Element& other) const {
981 return (other.getType() == Element::null);
982}
983
984bool
985StringElement::equals(const Element& other) const {
986 return (other.getType() == Element::string) &&
987 (s == other.stringValue());
988}
989
990bool
991ListElement::equals(const Element& other) const {
992 if (other.getType() == Element::list) {
993 const size_t s = size();
994 if (s != other.size()) {
995 return (false);
996 }
997 for (size_t i = 0; i < s; ++i) {
998 if (!get(i)->equals(*other.get(i))) {
999 return (false);
1000 }
1001 }
1002 return (true);
1003 } else {
1004 return (false);
1005 }
1006}
1007
1008bool
1009MapElement::equals(const Element& other) const {
1010 if (other.getType() == Element::map) {
1011 const std::map<std::string, ConstElementPtr>& m = mapValue();
1012 for (std::map<std::string, ConstElementPtr>::const_iterator it =
1013 m.begin();
1014 it != m.end() ; ++it) {
1015 if (other.contains((*it).first)) {
1016 if (!get((*it).first)->equals(*other.get((*it).first))) {
1017 return (false);
1018 }
1019 } else {
1020 return (false);
1021 }
1022 }
1023 // quickly walk through the other map too, to see if there's
1024 // anything in there that we don't have. We don't need to
1025 // compare those elements; if one of them is missing we
1026 // differ (and if it's not missing the loop above has checked
1027 // it)
1028 std::map<std::string, ConstElementPtr>::const_iterator it;
1029 for (it = other.mapValue().begin();
1030 it != other.mapValue().end();
1031 ++it) {
1032 if (!contains((*it).first)) {
1033 return (false);
1034 }
1035 }
1036 return (true);
1037 } else {
1038 return (false);
1039 }
1040}
1041
1042bool
1044 return (!p);
1045}
1046
1047void
1049 if (!b) {
1050 return;
1051 }
1052 if (a->getType() != Element::map || b->getType() != Element::map) {
1053 isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1054 }
1055
1056 // As maps do not allow entries with multiple keys, we can either iterate
1057 // over a checking for identical entries in b or vice-versa. As elements
1058 // are removed from a if a match is found, we choose to iterate over b to
1059 // avoid problems with element removal affecting the iterator.
1060 const std::map<std::string, ConstElementPtr>& m = b->mapValue();
1061 for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
1062 it != m.end() ; ++it) {
1063 if (a->contains((*it).first)) {
1064 if (a->get((*it).first)->equals(*b->get((*it).first))) {
1065 a->remove((*it).first);
1066 }
1067 }
1068 }
1069}
1070
1073 ElementPtr result = Element::createMap();
1074
1075 if (!b) {
1076 return (result);
1077 }
1078
1079 if (a->getType() != Element::map || b->getType() != Element::map) {
1080 isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1081 }
1082
1083 const std::map<std::string, ConstElementPtr>& m = a->mapValue();
1084 for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
1085 it != m.end() ; ++it) {
1086 if (!b->contains((*it).first) ||
1087 !a->get((*it).first)->equals(*b->get((*it).first))) {
1088 result->set((*it).first, (*it).second);
1089 }
1090 }
1091
1092 return (result);
1093}
1094
1095void
1097 if (element->getType() != Element::map ||
1098 other->getType() != Element::map) {
1099 isc_throw(TypeError, "merge arguments not MapElements");
1100 }
1101
1102 const std::map<std::string, ConstElementPtr>& m = other->mapValue();
1103 for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
1104 it != m.end() ; ++it) {
1105 if ((*it).second && (*it).second->getType() != Element::null) {
1106 element->set((*it).first, (*it).second);
1107 } else if (element->contains((*it).first)) {
1108 element->remove((*it).first);
1109 }
1110 }
1111}
1112
1114copy(ConstElementPtr from, int level) {
1115 if (isNull(from)) {
1116 isc_throw(BadValue, "copy got a null pointer");
1117 }
1118 int from_type = from->getType();
1119 if (from_type == Element::integer) {
1120 return (ElementPtr(new IntElement(from->intValue())));
1121 } else if (from_type == Element::real) {
1122 return (ElementPtr(new DoubleElement(from->doubleValue())));
1123 } else if (from_type == Element::boolean) {
1124 return (ElementPtr(new BoolElement(from->boolValue())));
1125 } else if (from_type == Element::null) {
1126 return (ElementPtr(new NullElement()));
1127 } else if (from_type == Element::string) {
1128 return (ElementPtr(new StringElement(from->stringValue())));
1129 } else if (from_type == Element::list) {
1130 ElementPtr result = ElementPtr(new ListElement());
1131 typedef std::vector<ElementPtr> ListType;
1132 const ListType& value = from->listValue();
1133 for (ListType::const_iterator it = value.cbegin();
1134 it != value.cend(); ++it) {
1135 if (level == 0) {
1136 result->add(*it);
1137 } else {
1138 result->add(copy(*it, level - 1));
1139 }
1140 }
1141 return (result);
1142 } else if (from_type == Element::map) {
1143 ElementPtr result = ElementPtr(new MapElement());
1144 typedef std::map<std::string, ConstElementPtr> MapType;
1145 const MapType& value = from->mapValue();
1146 for (MapType::const_iterator it = value.cbegin();
1147 it != value.cend(); ++it) {
1148 if (level == 0) {
1149 result->set(it->first, it->second);
1150 } else {
1151 result->set(it->first, copy(it->second, level - 1));
1152 }
1153 }
1154 return (result);
1155 } else {
1156 isc_throw(BadValue, "copy got an element of type: " << from_type);
1157 }
1158}
1159
1160namespace {
1161
1162// Helper function which blocks infinite recursion
1163bool
1164isEquivalent0(ConstElementPtr a, ConstElementPtr b, unsigned level)
1165{
1166 // check looping forever on cycles
1167 if (!level) {
1168 isc_throw(BadValue, "isEquivalent got infinite recursion: "
1169 "arguments include cycles");
1170 }
1171 if (!a || !b) {
1172 isc_throw(BadValue, "isEquivalent got a null pointer");
1173 }
1174 // check types
1175 if (a->getType() != b->getType()) {
1176 return (false);
1177 }
1178 if (a->getType() == Element::list) {
1179 // check empty
1180 if (a->empty()) {
1181 return (b->empty());
1182 }
1183 // check size
1184 if (a->size() != b->size()) {
1185 return (false);
1186 }
1187
1188 // copy b into a list
1189 const size_t s = a->size();
1190 typedef std::list<ConstElementPtr> ListType;
1191 ListType l;
1192 for (size_t i = 0; i < s; ++i) {
1193 l.push_back(b->get(i));
1194 }
1195
1196 // iterate on a
1197 for (size_t i = 0; i < s; ++i) {
1198 ConstElementPtr item = a->get(i);
1199 // lookup this item in the list
1200 bool found = false;
1201 for (ListType::iterator it = l.begin();
1202 it != l.end(); ++it) {
1203 // if found in the list remove it
1204 if (isEquivalent0(item, *it, level - 1)) {
1205 found = true;
1206 l.erase(it);
1207 break;
1208 }
1209 }
1210 // if not found argument differs
1211 if (!found) {
1212 return (false);
1213 }
1214 }
1215
1216 // sanity check: the list must be empty
1217 if (!l.empty()) {
1218 isc_throw(Unexpected, "isEquivalent internal error");
1219 }
1220 return (true);
1221 } else if (a->getType() == Element::map) {
1222 // iterate on the first map
1223 typedef std::map<std::string, ConstElementPtr> MapType;
1224 const MapType& ma = a->mapValue();
1225 for (MapType::const_iterator it = ma.begin();
1226 it != ma.end() ; ++it) {
1227 // get the b value for the given keyword and recurse
1228 ConstElementPtr item = b->get(it->first);
1229 if (!item || !isEquivalent0(it->second, item, level - 1)) {
1230 return (false);
1231 }
1232 }
1233 // iterate on the second map
1234 const MapType& mb = b->mapValue();
1235 for (MapType::const_iterator it = mb.begin();
1236 it != mb.end() ; ++it) {
1237 // check if the keyword exists
1238 if (!a->contains(it->first)) {
1239 return (false);
1240 }
1241 }
1242 return (true);
1243 } else {
1244 return (a->equals(*b));
1245 }
1246}
1247
1248}
1249
1250bool
1252 return (isEquivalent0(a, b, 100));
1253}
1254
1255void
1256prettyPrint(ConstElementPtr element, std::ostream& out,
1257 unsigned indent, unsigned step) {
1258 if (!element) {
1259 isc_throw(BadValue, "prettyPrint got a null pointer");
1260 }
1261 if (element->getType() == Element::list) {
1262 // empty list case
1263 if (element->empty()) {
1264 out << "[ ]";
1265 return;
1266 }
1267
1268 // complex ? multiline : oneline
1269 if (!element->get(0)) {
1270 isc_throw(BadValue, "prettyPrint got a null pointer");
1271 }
1272 int first_type = element->get(0)->getType();
1273 bool complex = false;
1274 if ((first_type == Element::list) || (first_type == Element::map)) {
1275 complex = true;
1276 }
1277 std::string separator = complex ? ",\n" : ", ";
1278
1279 // open the list
1280 out << "[" << (complex ? "\n" : " ");
1281
1282 // iterate on items
1283 typedef std::vector<ElementPtr> ListType;
1284 const ListType& l = element->listValue();
1285 for (ListType::const_iterator it = l.begin();
1286 it != l.end(); ++it) {
1287 // add the separator if not the first item
1288 if (it != l.begin()) {
1289 out << separator;
1290 }
1291 // add indentation
1292 if (complex) {
1293 out << std::string(indent + step, ' ');
1294 }
1295 // recursive call
1296 prettyPrint(*it, out, indent + step, step);
1297 }
1298
1299 // close the list
1300 if (complex) {
1301 out << "\n" << std::string(indent, ' ');
1302 } else {
1303 out << " ";
1304 }
1305 out << "]";
1306 } else if (element->getType() == Element::map) {
1307 // empty map case
1308 if (element->size() == 0) {
1309 out << "{ }";
1310 return;
1311 }
1312
1313 // open the map
1314 out << "{\n";
1315
1316 bool first = true;
1317 // output comment first
1318 if (element->contains("comment")) {
1319 // add indentation
1320 out << std::string(indent + step, ' ');
1321 // add keyword:
1322 out << "\"comment\": ";
1323 // recursive call
1324 prettyPrint(element->get("comment"), out, indent + step, step);
1325 // it was the first
1326 first = false;
1327 }
1328
1329 // iterate on keyword: value
1330 typedef std::map<std::string, ConstElementPtr> MapType;
1331 const MapType& m = element->mapValue();
1332 for (MapType::const_iterator it = m.begin();
1333 it != m.end(); ++it) {
1334 // skip comment
1335 if (it->first == "comment") {
1336 continue;
1337 }
1338 // add the separator if not the first item
1339 if (first) {
1340 first = false;
1341 } else {
1342 out << ",\n";
1343 }
1344 // add indentation
1345 out << std::string(indent + step, ' ');
1346 // add keyword:
1347 out << "\"" << it->first << "\": ";
1348 // recursive call
1349 prettyPrint(it->second, out, indent + step, step);
1350 }
1351
1352 // close the map
1353 out << "\n" << std::string(indent, ' ') << "}";
1354 } else {
1355 // not a list or a map
1356 element->toJSON(out);
1357 }
1358}
1359
1360std::string
1361prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
1362 std::stringstream ss;
1363 prettyPrint(element, ss, indent, step);
1364 return (ss.str());
1365}
1366
1367void Element::preprocess(std::istream& in, std::stringstream& out) {
1368
1369 std::string line;
1370
1371 while (std::getline(in, line)) {
1372 // If this is a comments line, replace it with empty line
1373 // (so the line numbers will still match
1374 if (!line.empty() && line[0] == '#') {
1375 line = "";
1376 }
1377
1378 // getline() removes end line characters. Unfortunately, we need
1379 // it for getting the line numbers right (in case we report an
1380 // error.
1381 out << line;
1382 out << "\n";
1383 }
1384}
1385
1386}
1387}
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:798
bool equals(const Element &other) const
Definition: data.cc:974
bool equals(const Element &other) const
Definition: data.cc:968
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:793
The Element class represents a piece of data, used by the command channel and configuration parts.
Definition: data.h:66
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:223
virtual bool equals(const Element &other) const =0
virtual bool getValue(int64_t &t) const
Definition: data.cc:70
static std::string typeToName(Element::types type)
Returns the name of the given type as a string.
Definition: data.cc:605
virtual int64_t intValue() const
Definition: data.h:211
std::string str() const
Returns a string representing the Element and all its child elements; note that this is different fro...
Definition: data.cc:51
virtual std::string stringValue() const
Definition: data.h:217
std::string toWire() const
Returns the wireformat for the Element and all its child elements.
Definition: data.cc:58
static ElementPtr fromWire(std::stringstream &in, int length)
These function pparse the wireformat at the given stringstream (of the given length).
Definition: data.cc:927
virtual bool setValue(const long long int v)
Definition: data.cc:100
static ElementPtr fromJSONFile(const std::string &file_name, bool preproc=false)
Reads contents of specified file and interprets it as JSON.
Definition: data.cc:769
virtual bool empty() const
Return true if there are no elements in the list.
Definition: data.cc:160
virtual void remove(const int i)
Removes the element at the given position.
Definition: data.cc:150
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.
Definition: data.cc:180
virtual ConstElementPtr find(const std::string &identifier) const
Recursively finds any data at the given identifier.
Definition: data.cc:185
virtual size_t size() const
Returns the number of elements in the list.
Definition: data.cc:155
virtual const std::map< std::string, ConstElementPtr > & mapValue() const
Definition: data.h:223
virtual void add(ElementPtr element)
Adds an ElementPtr to the list.
Definition: data.cc:145
virtual const std::vector< ElementPtr > & listValue() const
Definition: data.h:219
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition: data.cc:750
virtual ConstElementPtr get(const int i) const
Returns the ElementPtr at the given index.
Definition: data.cc:130
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:268
static Element::types nameToType(const std::string &type_name)
Converts the string to the corresponding type Throws a TypeError if the name is unknown.
Definition: data.cc:629
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:263
virtual void toJSON(std::ostream &ss) const =0
Converts the Element to JSON format and appends it to the given stringstream.
virtual void set(const size_t i, ElementPtr element)
Sets the ElementPtr at the given index.
Definition: data.cc:140
virtual double doubleValue() const
Definition: data.h:213
int getType() const
Definition: data.h:156
virtual bool boolValue() const
Definition: data.h:215
static void preprocess(std::istream &in, std::stringstream &out)
input text preprocessor
Definition: data.cc:1367
virtual ElementPtr getNonConst(const int i) const
returns element as non-const pointer
Definition: data.cc:135
Notes: IntElement type is changed to int64_t.
Definition: data.h:544
bool equals(const Element &other) const
Definition: data.cc:962
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:788
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:860
bool equals(const Element &other) const
Definition: data.cc:991
ConstElementPtr find(const std::string &id) const
Recursively finds any data at the given identifier.
Definition: data.cc:899
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:875
void set(const std::string &key, ConstElementPtr value)
Sets the ElementPtr at the given key.
Definition: data.cc:943
bool equals(const Element &other) const
Definition: data.cc:1009
bool equals(const Element &other) const
Definition: data.cc:980
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:807
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:812
bool equals(const Element &other) const
Definition: data.cc:985
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition: data.h:30
#define throwTypeError(error)
Add the position to a TypeError message should be used in place of isc_throw(TypeError,...
Definition: data.h:183
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1114
bool operator==(const Element &a, const Element &b)
Definition: data.cc:211
void removeIdentical(ElementPtr a, ConstElementPtr b)
Remove all values from the first ElementPtr that are equal in the second.
Definition: data.cc:1048
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1096
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
Definition: data.cc:1251
void prettyPrint(ConstElementPtr element, std::ostream &out, unsigned indent, unsigned step)
Pretty prints the data into stream.
Definition: data.cc:1256
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
Definition: data.cc:1043
std::ostream & operator<<(std::ostream &out, const Element::Position &pos)
Insert Element::Position as a string into stream.
Definition: data.cc:45
bool operator!=(const Element &a, const Element &b)
Definition: data.cc:215
boost::shared_ptr< Element > ElementPtr
Definition: data.h:22
Defines the logger used by the top-level component of kea-dhcp-ddns.
Represents the position of the data element within a configuration string.
Definition: data.h:88
uint32_t pos_
Position within the line.
Definition: data.h:91
std::string str() const
Returns the position in the textual format.
Definition: data.cc:38
uint32_t line_
Line number.
Definition: data.h:90
std::string file_
File name.
Definition: data.h:89