Kea 2.5.7
data.cc
Go to the documentation of this file.
1// Copyright (C) 2010-2024 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#include <util/bigints.h>
11
12#include <cstring>
13#include <cassert>
14#include <climits>
15#include <list>
16#include <map>
17#include <cstdio>
18#include <iostream>
19#include <iomanip>
20#include <string>
21#include <sstream>
22#include <fstream>
23#include <cerrno>
24
25#include <boost/lexical_cast.hpp>
26
27#include <cmath>
28
29using namespace std;
30
32
33namespace {
34const char* const WHITESPACE = " \b\f\n\r\t";
35} // end anonymous namespace
36
37namespace isc {
38namespace data {
39
40std::string
42 std::ostringstream ss;
43 ss << file_ << ":" << line_ << ":" << pos_;
44 return (ss.str());
45}
46
47std::ostream&
48operator<<(std::ostream& out, const Element::Position& pos) {
49 out << pos.str();
50 return (out);
51}
52
53std::string
54Element::str() const {
55 std::stringstream ss;
56 toJSON(ss);
57 return (ss.str());
58}
59
60std::string
62 std::stringstream ss;
63 toJSON(ss);
64 return (ss.str());
65}
66
67void
68Element::toWire(std::ostream& ss) const {
69 toJSON(ss);
70}
71
72bool
73Element::getValue(int64_t&) const {
74 return (false);
75}
76
77bool
78Element::getValue(double&) const {
79 return (false);
80}
81
82bool
83Element::getValue(bool&) const {
84 return (false);
85}
86
87bool
88Element::getValue(std::string&) const {
89 return (false);
90}
91
92bool
93Element::getValue(std::vector<ElementPtr>&) const {
94 return (false);
95}
96
97bool
98Element::getValue(std::map<std::string, ConstElementPtr>&) const {
99 return (false);
100}
101
102bool
103Element::setValue(const long long int) {
104 return (false);
105}
106
107bool
109 return (false);
110}
111
112bool
113Element::setValue(const double) {
114 return (false);
115}
116
117bool
118Element::setValue(const bool) {
119 return (false);
120}
121
122bool
123Element::setValue(const std::string&) {
124 return (false);
125}
126
127bool
128Element::setValue(const std::vector<ElementPtr>&) {
129 return (false);
130}
131
132bool
133Element::setValue(const std::map<std::string, ConstElementPtr>&) {
134 return (false);
135}
136
138Element::get(const int) const {
139 throwTypeError("get(int) called on a non-container Element");
140}
141
143Element::getNonConst(const int) const {
144 throwTypeError("get(int) called on a non-container Element");
145}
146
147void
148Element::set(const size_t, ElementPtr) {
149 throwTypeError("set(int, element) called on a non-list Element");
150}
151
152void
154 throwTypeError("add() called on a non-list Element");
155}
156
157void
158Element::remove(const int) {
159 throwTypeError("remove(int) called on a non-container Element");
160}
161
162size_t
164 throwTypeError("size() called on a non-list Element");
165}
166
167bool
169 throwTypeError("empty() called on a non-container Element");
170}
171
173Element::get(const std::string&) const {
174 throwTypeError("get(string) called on a non-map Element");
175}
176
177void
178Element::set(const std::string&, ConstElementPtr) {
179 throwTypeError("set(name, element) called on a non-map Element");
180}
181
182void
183Element::remove(const std::string&) {
184 throwTypeError("remove(string) called on a non-map Element");
185}
186
187bool
188Element::contains(const std::string&) const {
189 throwTypeError("contains(string) called on a non-map Element");
190}
191
193Element::find(const std::string&) const {
194 throwTypeError("find(string) called on a non-map Element");
195}
196
197bool
198Element::find(const std::string&, ConstElementPtr&) const {
199 return (false);
200}
201
202namespace {
203inline void
204throwJSONError(const std::string& error, const std::string& file, int line,
205 int pos) {
206 std::stringstream ss;
207 ss << error << " in " + file + ":" << line << ":" << pos;
208 isc_throw(JSONError, ss.str());
209}
210} // end anonymous namespace
211
212std::ostream&
213operator<<(std::ostream& out, const Element& e) {
214 return (out << e.str());
215}
216
217bool
218operator==(const Element& a, const Element& b) {
219 return (a.equals(b));
220}
221
222bool operator!=(const Element& a, const Element& b) {
223 return (!a.equals(b));
224}
225
226bool
227operator<(Element const& a, Element const& b) {
228 if (a.getType() != b.getType()) {
229 isc_throw(BadValue, "cannot compare Elements of different types");
230 }
231 switch (a.getType()) {
232 case Element::integer:
233 return a.intValue() < b.intValue();
234 case Element::real:
235 return a.doubleValue() < b.doubleValue();
236 case Element::boolean:
237 return b.boolValue() || !a.boolValue();
238 case Element::string:
239 return std::strcmp(a.stringValue().c_str(), b.stringValue().c_str()) < 0;
240 default:
241 isc_throw(BadValue, "cannot compare Elements of type " << to_string(a.getType()));
242 }
243}
244
245//
246// factory functions
247//
250 return (ElementPtr(new NullElement(pos)));
251}
252
254Element::create(const long long int i, const Position& pos) {
255 return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
256}
257
260 return (ElementPtr(new BigIntElement(i, pos)));
261}
262
264Element::create(const int i, const Position& pos) {
265 return (create(static_cast<long long int>(i), pos));
269Element::create(const long int i, const Position& pos) {
270 return (create(static_cast<long long int>(i), pos));
271}
272
274Element::create(const uint32_t i, const Position& pos) {
275 return (create(static_cast<long long int>(i), pos));
276}
277
279Element::create(const double d, const Position& pos) {
280 return (ElementPtr(new DoubleElement(d, pos)));
281}
284Element::create(const bool b, const Position& pos) {
285 return (ElementPtr(new BoolElement(b, pos)));
289Element::create(const std::string& s, const Position& pos) {
290 return (ElementPtr(new StringElement(s, pos)));
291}
292
294Element::create(const char *s, const Position& pos) {
295 return (create(std::string(s), pos));
296}
297
300 return (ElementPtr(new ListElement(pos)));
301}
302
305 return (ElementPtr(new MapElement(pos)));
306}
307
308
309//
310// helper functions for fromJSON factory
311//
312namespace {
313bool
314charIn(const int c, const char* chars) {
315 const size_t chars_len = std::strlen(chars);
316 for (size_t i = 0; i < chars_len; ++i) {
317 if (chars[i] == c) {
318 return (true);
319 }
320 }
321 return (false);
322}
323
324void
325skipChars(std::istream& in, const char* chars, int& line, int& pos) {
326 int c = in.peek();
327 while (charIn(c, chars) && c != EOF) {
328 if (c == '\n') {
329 ++line;
330 pos = 1;
331 } else {
332 ++pos;
333 }
334 in.ignore();
335 c = in.peek();
336 }
337}
338
339// skip on the input stream to one of the characters in chars
340// if another character is found this function throws JSONError
341// unless that character is specified in the optional may_skip
343// It returns the found character (as an int value).
344int
345skipTo(std::istream& in, const std::string& file, int& line, int& pos,
346 const char* chars, const char* may_skip="") {
347 int c = in.get();
348 ++pos;
349 while (c != EOF) {
350 if (c == '\n') {
351 pos = 1;
352 ++line;
353 }
354 if (charIn(c, may_skip)) {
355 c = in.get();
356 ++pos;
357 } else if (charIn(c, chars)) {
358 while (charIn(in.peek(), may_skip)) {
359 if (in.peek() == '\n') {
360 pos = 1;
361 ++line;
362 } else {
363 ++pos;
364 }
365 in.ignore();
366 }
367 return (c);
368 } else {
369 throwJSONError(std::string("'") + std::string(1, c) + "' read, one of \"" + chars + "\" expected", file, line, pos);
370 }
371 }
372 throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
373 return (c); // shouldn't reach here, but some compilers require it
374}
375
376// TODO: Should we check for all other official escapes here (and
377// error on the rest)?
378std::string
379strFromStringstream(std::istream& in, const std::string& file,
380 const int line, int& pos) {
381 std::stringstream ss;
382 int c = in.get();
383 ++pos;
384 if (c == '"') {
385 c = in.get();
386 ++pos;
387 } else {
388 throwJSONError("String expected", file, line, pos);
389 }
390
391 while (c != EOF && c != '"') {
392 if (c == '\\') {
393 // see the spec for allowed escape characters
394 int d;
395 switch (in.peek()) {
396 case '"':
397 c = '"';
398 break;
399 case '/':
400 c = '/';
401 break;
402 case '\\':
403 c = '\\';
404 break;
405 case 'b':
406 c = '\b';
407 break;
408 case 'f':
409 c = '\f';
410 break;
411 case 'n':
412 c = '\n';
413 break;
414 case 'r':
415 c = '\r';
416 break;
417 case 't':
418 c = '\t';
419 break;
420 case 'u':
421 // skip first 0
422 in.ignore();
423 ++pos;
424 c = in.peek();
425 if (c != '0') {
426 throwJSONError("Unsupported unicode escape", file, line, pos);
427 }
428 // skip second 0
429 in.ignore();
430 ++pos;
431 c = in.peek();
432 if (c != '0') {
433 throwJSONError("Unsupported unicode escape", file, line, pos - 2);
434 }
435 // get first digit
436 in.ignore();
437 ++pos;
438 d = in.peek();
439 if ((d >= '0') && (d <= '9')) {
440 c = (d - '0') << 4;
441 } else if ((d >= 'A') && (d <= 'F')) {
442 c = (d - 'A' + 10) << 4;
443 } else if ((d >= 'a') && (d <= 'f')) {
444 c = (d - 'a' + 10) << 4;
445 } else {
446 throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 3);
447 }
448 // get second digit
449 in.ignore();
450 ++pos;
451 d = in.peek();
452 if ((d >= '0') && (d <= '9')) {
453 c |= d - '0';
454 } else if ((d >= 'A') && (d <= 'F')) {
455 c |= d - 'A' + 10;
456 } else if ((d >= 'a') && (d <= 'f')) {
457 c |= d - 'a' + 10;
458 } else {
459 throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 4);
460 }
461 break;
462 default:
463 throwJSONError("Bad escape", file, line, pos);
464 }
465 // drop the escaped char
466 in.ignore();
467 ++pos;
468 }
469 ss.put(c);
470 c = in.get();
471 ++pos;
472 }
473 if (c == EOF) {
474 throwJSONError("Unterminated string", file, line, pos);
475 }
476 return (ss.str());
477}
478
479std::string
480wordFromStringstream(std::istream& in, int& pos) {
481 std::stringstream ss;
482 while (isalpha(in.peek())) {
483 ss << (char) in.get();
484 }
485 pos += ss.str().size();
486 return (ss.str());
487}
488
489std::string
490numberFromStringstream(std::istream& in, int& pos) {
491 std::stringstream ss;
492 while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
493 in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
494 ss << (char) in.get();
495 }
496 pos += ss.str().size();
497 return (ss.str());
498}
499
500// Should we change from IntElement and DoubleElement to NumberElement
501// that can also hold an e value? (and have specific getters if the
502// value is larger than an int can handle)
503//
504// At the moment of writing, the only way that the code flow can reach the
505// int128_t cast, and not throw, is by retrieving one of the few bigint
506// statistics through kea-ctrl-agent. If kea-ctrl-agent ever gets removed, and
507// its HTTP listener embedded in Kea, then the cast to int128_t can be removed as
508// well, as there is no deserialization of bigints required, although the only
509// benefit would be better performance for error cases, so it's arguable.
511fromStringstreamNumber(std::istream& in, const std::string& file,
512 const int line, int& pos) {
513 // Remember position where the value starts. It will be set in the
514 // Position structure of the Element to be created.
515 const uint32_t start_pos = pos;
516 // This will move the pos to the end of the value.
517 const std::string number = numberFromStringstream(in, pos);
518
519 // Is it a double?
520 if (number.find_first_of(".eE") < number.size()) {
521 try {
522 return (Element::create(boost::lexical_cast<double>(number),
523 Element::Position(file, line, start_pos)));
524 } catch (const boost::bad_lexical_cast& exception) {
525 throwJSONError("Number overflow while trying to cast '" + number +
526 "' to double: " + exception.what(),
527 file, line, start_pos);
528 }
529 }
530
531 // Is it an integer?
532 try {
533 return (Element::create(boost::lexical_cast<int64_t>(number),
534 Element::Position(file, line, start_pos)));
535 } catch (const boost::bad_lexical_cast& exception64) {
536 // Is it a big integer?
537 try {
538 return (Element::create(int128_t(number),
539 Element::Position(file, line, start_pos)));
540 } catch (overflow_error const& exception128) {
541 throwJSONError("Number overflow while trying to cast '" + number +
542 "' to int64 and subsequently to int128: " +
543 exception64.what() + ", " + exception128.what(),
544 file, line, start_pos);
545 }
546 }
547 return (ElementPtr());
548}
549
551fromStringstreamBool(std::istream& in, const std::string& file,
552 const int line, int& pos) {
553 // Remember position where the value starts. It will be set in the
554 // Position structure of the Element to be created.
555 const uint32_t start_pos = pos;
556 // This will move the pos to the end of the value.
557 const std::string word = wordFromStringstream(in, pos);
558
559 if (word == "true") {
560 return (Element::create(true, Element::Position(file, line,
561 start_pos)));
562 } else if (word == "false") {
563 return (Element::create(false, Element::Position(file, line,
564 start_pos)));
565 } else {
566 throwJSONError(std::string("Bad boolean value: ") + word, file,
567 line, start_pos);
568 }
569 return (ElementPtr());
570}
571
573fromStringstreamNull(std::istream& in, const std::string& file,
574 const int line, int& pos) {
575 // Remember position where the value starts. It will be set in the
576 // Position structure of the Element to be created.
577 const uint32_t start_pos = pos;
578 // This will move the pos to the end of the value.
579 const std::string word = wordFromStringstream(in, pos);
580 if (word == "null") {
581 return (Element::create(Element::Position(file, line, start_pos)));
582 } else {
583 throwJSONError(std::string("Bad null value: ") + word, file,
584 line, start_pos);
585 return (ElementPtr());
586 }
587}
588
590fromStringstreamString(std::istream& in, const std::string& file, int& line,
591 int& pos) {
592 // Remember position where the value starts. It will be set in the
593 // Position structure of the Element to be created.
594 const uint32_t start_pos = pos;
595 // This will move the pos to the end of the value.
596 const std::string string_value = strFromStringstream(in, file, line, pos);
597 return (Element::create(string_value, Element::Position(file, line,
598 start_pos)));
599}
600
602fromStringstreamList(std::istream& in, const std::string& file, int& line,
603 int& pos) {
604 int c = 0;
605 ElementPtr list = Element::createList(Element::Position(file, line, pos));
606 ElementPtr cur_list_element;
607
608 skipChars(in, WHITESPACE, line, pos);
609 while (c != EOF && c != ']') {
610 if (in.peek() != ']') {
611 cur_list_element = Element::fromJSON(in, file, line, pos);
612 list->add(cur_list_element);
613 c = skipTo(in, file, line, pos, ",]", WHITESPACE);
614 } else {
615 c = in.get();
616 ++pos;
617 }
618 }
619 return (list);
620}
621
623fromStringstreamMap(std::istream& in, const std::string& file, int& line,
624 int& pos) {
625 ElementPtr map = Element::createMap(Element::Position(file, line, pos));
626 skipChars(in, WHITESPACE, line, pos);
627 int c = in.peek();
628 if (c == EOF) {
629 throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
630 } else if (c == '}') {
631 // empty map, skip closing curly
632 in.ignore();
633 } else {
634 while (c != EOF && c != '}') {
635 std::string key = strFromStringstream(in, file, line, pos);
636
637 skipTo(in, file, line, pos, ":", WHITESPACE);
638 // skip the :
639
640 ConstElementPtr value = Element::fromJSON(in, file, line, pos);
641 map->set(key, value);
642
643 c = skipTo(in, file, line, pos, ",}", WHITESPACE);
644 }
645 }
646 return (map);
647}
648} // end anonymous namespace
649
650std::string
652 switch (type) {
653 case Element::integer:
654 return (std::string("integer"));
655 case Element::bigint:
656 return (std::string("bigint"));
657 case Element::real:
658 return (std::string("real"));
659 case Element::boolean:
660 return (std::string("boolean"));
661 case Element::string:
662 return (std::string("string"));
663 case Element::list:
664 return (std::string("list"));
665 case Element::map:
666 return (std::string("map"));
667 case Element::null:
668 return (std::string("null"));
669 case Element::any:
670 return (std::string("any"));
671 default:
672 return (std::string("unknown"));
673 }
674}
675
677Element::nameToType(const std::string& type_name) {
678 if (type_name == "integer") {
679 return (Element::integer);
680 } else if (type_name == "bigint") {
681 return (Element::bigint);
682 } else if (type_name == "real") {
683 return (Element::real);
684 } else if (type_name == "boolean") {
685 return (Element::boolean);
686 } else if (type_name == "string") {
687 return (Element::string);
688 } else if (type_name == "list") {
689 return (Element::list);
690 } else if (type_name == "map") {
691 return (Element::map);
692 } else if (type_name == "named_set") {
693 return (Element::map);
694 } else if (type_name == "null") {
695 return (Element::null);
696 } else if (type_name == "any") {
697 return (Element::any);
698 } else {
699 isc_throw(TypeError, type_name + " is not a valid type name");
700 }
701}
702
704Element::fromJSON(std::istream& in, bool preproc) {
705
706 int line = 1, pos = 1;
707 stringstream filtered;
708 if (preproc) {
709 preprocess(in, filtered);
710 }
711
712 ElementPtr value = fromJSON(preproc ? filtered : in, "<istream>", line, pos);
713
714 return (value);
715}
716
718Element::fromJSON(std::istream& in, const std::string& file_name, bool preproc) {
719 int line = 1, pos = 1;
720 stringstream filtered;
721 if (preproc) {
722 preprocess(in, filtered);
723 }
724 return (fromJSON(preproc ? filtered : in, file_name, line, pos));
725}
726
728Element::fromJSON(std::istream& in, const std::string& file, int& line,
729 int& pos) {
730 int c = 0;
731 ElementPtr element;
732 bool el_read = false;
733 skipChars(in, WHITESPACE, line, pos);
734 while (c != EOF && !el_read) {
735 c = in.get();
736 pos++;
737 switch(c) {
738 case '1':
739 case '2':
740 case '3':
741 case '4':
742 case '5':
743 case '6':
744 case '7':
745 case '8':
746 case '9':
747 case '0':
748 case '-':
749 case '+':
750 case '.':
751 in.putback(c);
752 --pos;
753 element = fromStringstreamNumber(in, file, line, pos);
754 el_read = true;
755 break;
756 case 't':
757 case 'f':
758 in.putback(c);
759 --pos;
760 element = fromStringstreamBool(in, file, line, pos);
761 el_read = true;
762 break;
763 case 'n':
764 in.putback(c);
765 --pos;
766 element = fromStringstreamNull(in, file, line, pos);
767 el_read = true;
768 break;
769 case '"':
770 in.putback('"');
771 --pos;
772 element = fromStringstreamString(in, file, line, pos);
773 el_read = true;
774 break;
775 case '[':
776 element = fromStringstreamList(in, file, line, pos);
777 el_read = true;
778 break;
779 case '{':
780 element = fromStringstreamMap(in, file, line, pos);
781 el_read = true;
782 break;
783 case EOF:
784 break;
785 default:
786 throwJSONError(std::string("error: unexpected character ") + std::string(1, c), file, line, pos);
787 break;
788 }
789 }
790 if (el_read) {
791 return (element);
792 } else {
793 isc_throw(JSONError, "nothing read");
794 }
795}
796
798Element::fromJSON(const std::string& in, bool preproc) {
799 std::stringstream ss;
800 ss << in;
801
802 int line = 1, pos = 1;
803 stringstream filtered;
804 if (preproc) {
805 preprocess(ss, filtered);
806 }
807 ElementPtr result(fromJSON(preproc ? filtered : ss, "<string>", line, pos));
808 skipChars(ss, WHITESPACE, line, pos);
809 // ss must now be at end
810 if (ss.peek() != EOF) {
811 throwJSONError("Extra data", "<string>", line, pos);
812 }
813 return result;
814}
815
817Element::fromJSONFile(const std::string& file_name, bool preproc) {
818 // zero out the errno to be safe
819 errno = 0;
820
821 std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
822 if (!infile.is_open()) {
823 const char* error = strerror(errno);
824 isc_throw(InvalidOperation, "failed to read file '" << file_name
825 << "': " << error);
826 }
827
828 return (fromJSON(infile, file_name, preproc));
829}
830
831// to JSON format
832
833void
834IntElement::toJSON(std::ostream& ss) const {
835 ss << intValue();
836}
837
838void
839BigIntElement::toJSON(std::ostream& ss) const {
840 ss << bigIntValue();
841}
842
843void
844DoubleElement::toJSON(std::ostream& ss) const {
845 // The default output for doubles nicely drops off trailing
846 // zeros, however this produces strings without decimal points
847 // for whole number values. When reparsed this will create
848 // IntElements not DoubleElements. Rather than used a fixed
849 // precision, we'll just tack on an ".0" when the decimal point
850 // is missing.
851 ostringstream val_ss;
852 val_ss << doubleValue();
853 ss << val_ss.str();
854 if (val_ss.str().find_first_of('.') == string::npos) {
855 ss << ".0";
856 }
857}
858
859void
860BoolElement::toJSON(std::ostream& ss) const {
861 if (boolValue()) {
862 ss << "true";
863 } else {
864 ss << "false";
865 }
866}
867
868void
869NullElement::toJSON(std::ostream& ss) const {
870 ss << "null";
871}
872
873void
874StringElement::toJSON(std::ostream& ss) const {
875 ss << "\"";
876 const std::string& str = stringValue();
877 for (size_t i = 0; i < str.size(); ++i) {
878 const char c = str[i];
879 // Escape characters as defined in JSON spec
880 // Note that we do not escape forward slash; this
881 // is allowed, but not mandatory.
882 switch (c) {
883 case '"':
884 ss << '\\' << c;
885 break;
886 case '\\':
887 ss << '\\' << c;
888 break;
889 case '\b':
890 ss << '\\' << 'b';
891 break;
892 case '\f':
893 ss << '\\' << 'f';
894 break;
895 case '\n':
896 ss << '\\' << 'n';
897 break;
898 case '\r':
899 ss << '\\' << 'r';
900 break;
901 case '\t':
902 ss << '\\' << 't';
903 break;
904 default:
905 if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
906 std::ostringstream esc;
907 esc << "\\u"
908 << hex
909 << setw(4)
910 << setfill('0')
911 << (static_cast<unsigned>(c) & 0xff);
912 ss << esc.str();
913 } else {
914 ss << c;
915 }
916 }
917 }
918 ss << "\"";
919}
920
921void
922ListElement::toJSON(std::ostream& ss) const {
923 ss << "[ ";
924
925 const std::vector<ElementPtr>& v = listValue();
926 bool first = true;
927 for (auto const& it : v) {
928 if (!first) {
929 ss << ", ";
930 } else {
931 first = false;
932 }
933 it->toJSON(ss);
934 }
935 ss << " ]";
936}
937
938void
939MapElement::toJSON(std::ostream& ss) const {
940 ss << "{ ";
941
942 bool first = true;
943 for (auto const& it : m) {
944 if (!first) {
945 ss << ", ";
946 } else {
947 first = false;
948 }
949 ss << "\"" << it.first << "\": ";
950 if (it.second) {
951 it.second->toJSON(ss);
952 } else {
953 ss << "None";
954 }
955 }
956 ss << " }";
957}
958
959// throws when one of the types in the path (except the one
960// we're looking for) is not a MapElement
961// returns 0 if it could simply not be found
962// should that also be an exception?
964MapElement::find(const std::string& id) const {
965 const size_t sep = id.find('/');
966 if (sep == std::string::npos) {
967 return (get(id));
968 } else {
969 ConstElementPtr ce = get(id.substr(0, sep));
970 if (ce) {
971 // ignore trailing slash
972 if (sep + 1 != id.size()) {
973 return (ce->find(id.substr(sep + 1)));
974 } else {
975 return (ce);
976 }
977 } else {
978 return (ElementPtr());
979 }
980 }
981}
982
984Element::fromWire(const std::string& s) {
985 std::stringstream ss;
986 ss << s;
987 int line = 0, pos = 0;
988 return (fromJSON(ss, "<wire>", line, pos));
989}
990
992Element::fromWire(std::stringstream& in, int) {
993 //
994 // Check protocol version
995 //
996 //for (int i = 0 ; i < 4 ; ++i) {
997 // const unsigned char version_byte = get_byte(in);
998 // if (PROTOCOL_VERSION[i] != version_byte) {
999 // throw DecodeError("Protocol version incorrect");
1000 // }
1001 //}
1002 //length -= 4;
1003 int line = 0, pos = 0;
1004 return (fromJSON(in, "<wire>", line, pos));
1005}
1006
1007void
1008MapElement::set(const std::string& key, ConstElementPtr value) {
1009 m[key] = value;
1010}
1011
1012bool
1013MapElement::find(const std::string& id, ConstElementPtr& t) const {
1014 try {
1015 ConstElementPtr p = find(id);
1016 if (p) {
1017 t = p;
1018 return (true);
1019 }
1020 } catch (const TypeError&) {
1021 // ignore
1022 }
1023 return (false);
1024}
1025
1026bool
1027IntElement::equals(const Element& other) const {
1028 // Let's not be very picky with constraining the integer types to be the
1029 // same. Equality is sometimes checked from high-up in the Element hierarcy.
1030 // That is a context which, most of the time, does not have information on
1031 // the type of integers stored on Elements lower in the hierarchy. So it
1032 // would be difficult to differentiate between the integer types.
1033 return (other.getType() == Element::integer && i == other.intValue()) ||
1034 (other.getType() == Element::bigint && i == other.bigIntValue());
1035}
1036
1037bool
1038BigIntElement::equals(const Element& other) const {
1039 // Let's not be very picky with constraining the integer types to be the
1040 // same. Equality is sometimes checked from high-up in the Element hierarcy.
1041 // That is a context which, most of the time, does not have information on
1042 // the type of integers stored on Elements lower in the hierarchy. So it
1043 // would be difficult to differentiate between the integer types.
1044 return (other.getType() == Element::bigint && i_ == other.bigIntValue()) ||
1045 (other.getType() == Element::integer && i_ == other.intValue());
1046}
1047
1048bool
1049DoubleElement::equals(const Element& other) const {
1050 return (other.getType() == Element::real) &&
1051 (fabs(d - other.doubleValue()) < 1e-14);
1052}
1053
1054bool
1055BoolElement::equals(const Element& other) const {
1056 return (other.getType() == Element::boolean) &&
1057 (b == other.boolValue());
1058}
1059
1060bool
1061NullElement::equals(const Element& other) const {
1062 return (other.getType() == Element::null);
1063}
1064
1065bool
1066StringElement::equals(const Element& other) const {
1067 return (other.getType() == Element::string) &&
1068 (s == other.stringValue());
1069}
1070
1071bool
1072ListElement::equals(const Element& other) const {
1073 if (other.getType() == Element::list) {
1074 const size_t s = size();
1075 if (s != other.size()) {
1076 return (false);
1077 }
1078 for (size_t i = 0; i < s; ++i) {
1079 if (!get(i)->equals(*other.get(i))) {
1080 return (false);
1081 }
1082 }
1083 return (true);
1084 } else {
1085 return (false);
1086 }
1087}
1088
1089void
1090ListElement::sort(std::string const& index /* = std::string() */) {
1091 if (l.empty()) {
1092 return;
1093 }
1094
1095 int const t(l.at(0)->getType());
1096 std::function<bool(ElementPtr, ElementPtr)> comparator;
1097 if (t == map) {
1098 if (index.empty()) {
1099 isc_throw(BadValue, "index required when sorting maps");
1100 }
1101 comparator = [&](ElementPtr const& a, ElementPtr const& b) {
1102 ConstElementPtr const& ai(a->get(index));
1103 ConstElementPtr const& bi(b->get(index));
1104 if (ai && bi) {
1105 return *ai < *bi;
1106 }
1107 return true;
1108 };
1109 } else if (t == list) {
1110 // Nested lists. Not supported.
1111 return;
1112 } else {
1113 // Assume scalars.
1114 if (!index.empty()) {
1115 isc_throw(BadValue, "index given when sorting scalars?");
1116 }
1117 comparator = [&](ElementPtr const& a, ElementPtr const& b) {
1118 return *a < *b;
1119 };
1120 }
1121
1122 std::sort(l.begin(), l.end(), comparator);
1123}
1124
1125bool
1126MapElement::equals(const Element& other) const {
1127 if (other.getType() == Element::map) {
1128 if (size() != other.size()) {
1129 return (false);
1130 }
1131 for (auto const& kv : mapValue()) {
1132 auto key = kv.first;
1133 if (other.contains(key)) {
1134 if (!get(key)->equals(*other.get(key))) {
1135 return (false);
1136 }
1137 } else {
1138 return (false);
1139 }
1140 }
1141 return (true);
1142 } else {
1143 return (false);
1144 }
1145}
1146
1147bool
1149 return (!p);
1150}
1151
1152void
1154 if (!b) {
1155 return;
1156 }
1157 if (a->getType() != Element::map || b->getType() != Element::map) {
1158 isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1159 }
1160
1161 // As maps do not allow entries with multiple keys, we can either iterate
1162 // over a checking for identical entries in b or vice-versa. As elements
1163 // are removed from a if a match is found, we choose to iterate over b to
1164 // avoid problems with element removal affecting the iterator.
1165 for (auto const& kv : b->mapValue()) {
1166 auto key = kv.first;
1167 if (a->contains(key)) {
1168 if (a->get(key)->equals(*b->get(key))) {
1169 a->remove(key);
1170 }
1171 }
1172 }
1173}
1174
1177 ElementPtr result = Element::createMap();
1178
1179 if (!b) {
1180 return (result);
1181 }
1182
1183 if (a->getType() != Element::map || b->getType() != Element::map) {
1184 isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1185 }
1186
1187 for (auto const& kv : a->mapValue()) {
1188 auto key = kv.first;
1189 if (!b->contains(key) ||
1190 !a->get(key)->equals(*b->get(key))) {
1191 result->set(key, kv.second);
1192 }
1193 }
1194
1195 return (result);
1196}
1197
1198void
1200 if (element->getType() != Element::map ||
1201 other->getType() != Element::map) {
1202 isc_throw(TypeError, "merge arguments not MapElements");
1203 }
1204
1205 for (auto const& kv : other->mapValue()) {
1206 auto key = kv.first;
1207 auto value = kv.second;
1208 if (value && value->getType() != Element::null) {
1209 element->set(key, value);
1210 } else if (element->contains(key)) {
1211 element->remove(key);
1212 }
1213 }
1214}
1215
1216void
1218 HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
1219 if (element->getType() != other->getType()) {
1220 isc_throw(TypeError, "mergeDiffAdd arguments not same type");
1221 }
1222
1223 if (element->getType() == Element::list) {
1224 // Store new elements in a separate container so we don't overwrite
1225 // options as we add them (if there are duplicates).
1226 ElementPtr new_elements = Element::createList();
1227 for (auto const& right : other->listValue()) {
1228 // Check if we have any description of the key in the configuration
1229 // hierarchy.
1230 auto f = hierarchy[idx].find(key);
1231 if (f != hierarchy[idx].end()) {
1232 bool found = false;
1233 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1234 for (auto const& left : element->listValue()) {
1235 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1236 // Check if the elements refer to the same configuration
1237 // entity.
1238 if (f->second.match_(mutable_left, mutable_right)) {
1239 found = true;
1240 mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
1241 }
1242 }
1243 if (!found) {
1244 new_elements->add(right);
1245 }
1246 } else {
1247 new_elements->add(right);
1248 }
1249 }
1250 // Finally add the new elements.
1251 for (auto const& right : new_elements->listValue()) {
1252 element->add(right);
1253 }
1254 return;
1255 }
1256
1257 if (element->getType() == Element::map) {
1258 for (auto const& kv : other->mapValue()) {
1259 auto current_key = kv.first;
1260 auto value = boost::const_pointer_cast<Element>(kv.second);
1261 if (value && value->getType() != Element::null) {
1262 if (element->contains(current_key) &&
1263 (value->getType() == Element::map ||
1264 value->getType() == Element::list)) {
1265 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1266 mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
1267 } else {
1268 element->set(current_key, value);
1269 }
1270 }
1271 }
1272 return;
1273 }
1274 element = other;
1275}
1276
1277void
1279 HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
1280 if (element->getType() != other->getType()) {
1281 isc_throw(TypeError, "mergeDiffDel arguments not same type");
1282 }
1283
1284 if (element->getType() == Element::list) {
1285 for (auto const& value : other->listValue()) {
1286 ElementPtr mutable_right = boost::const_pointer_cast<Element>(value);
1287 for (uint32_t iter = 0; iter < element->listValue().size();) {
1288 bool removed = false;
1289 // Check if we have any description of the key in the
1290 // configuration hierarchy.
1291 auto f = hierarchy[idx].find(key);
1292 if (f != hierarchy[idx].end()) {
1293 ElementPtr mutable_left = boost::const_pointer_cast<Element>(element->listValue().at(iter));
1294 // Check if the elements refer to the same configuration
1295 // entity.
1296 if (f->second.match_(mutable_left, mutable_right)) {
1297 // Check if the user supplied data only contains
1298 // identification information, so the intent is to
1299 // delete the element, not just element data.
1300 if (f->second.no_data_(mutable_right)) {
1301 element->remove(iter);
1302 removed = true;
1303 } else {
1304 mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
1305 if (mutable_left->empty()) {
1306 element->remove(iter);
1307 removed = true;
1308 }
1309 }
1310 }
1311 } else if (element->listValue().at(iter)->equals(*value)) {
1312 element->remove(iter);
1313 removed = true;
1314 }
1315 if (!removed) {
1316 ++iter;
1317 }
1318 }
1319 }
1320 return;
1321 }
1322
1323 if (element->getType() == Element::map) {
1324 // If the resulting element still contains data, we need to restore the
1325 // key parameters, so we store them here.
1326 ElementPtr new_elements = Element::createMap();
1327 for (auto const& kv : other->mapValue()) {
1328 auto current_key = kv.first;
1329 auto value = boost::const_pointer_cast<Element>(kv.second);
1330 if (value && value->getType() != Element::null) {
1331 if (element->contains(current_key)) {
1332 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1333 if (mutable_element->getType() == Element::map ||
1334 mutable_element->getType() == Element::list) {
1335 mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
1336 if (mutable_element->empty()) {
1337 element->remove(current_key);
1338 }
1339 } else {
1340 // Check if we have any description of the key in the
1341 // configuration hierarchy.
1342 auto f = hierarchy[idx].find(key);
1343 if (f != hierarchy[idx].end()) {
1344 // Check if the key is used for element
1345 // identification.
1346 if (f->second.is_key_(current_key)) {
1347 // Store the key parameter.
1348 new_elements->set(current_key, mutable_element);
1349 }
1350 }
1351 element->remove(current_key);
1352 }
1353 }
1354 }
1355 }
1356 // If the element still contains data, restore the key elements.
1357 if (element->size()) {
1358 for (auto const& kv : new_elements->mapValue()) {
1359 element->set(kv.first, kv.second);
1360 }
1361 }
1362 return;
1363 }
1364 element = ElementPtr(new NullElement());
1365}
1366
1367void
1368extend(const std::string& container, const std::string& extension,
1369 ElementPtr& element, ElementPtr& other, HierarchyDescriptor& hierarchy,
1370 std::string key, size_t idx, bool alter) {
1371 if (element->getType() != other->getType()) {
1372 isc_throw(TypeError, "extend arguments not same type");
1373 }
1374
1375 if (element->getType() == Element::list) {
1376 for (auto const& right : other->listValue()) {
1377 // Check if we have any description of the key in the configuration
1378 // hierarchy.
1379 auto f = hierarchy[idx].find(key);
1380 if (f != hierarchy[idx].end()) {
1381 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1382 for (auto const& left : element->listValue()) {
1383 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1384 if (container == key) {
1385 alter = true;
1386 }
1387 if (f->second.match_(mutable_left, mutable_right)) {
1388 extend(container, extension, mutable_left, mutable_right,
1389 hierarchy, key, idx, alter);
1390 }
1391 }
1392 }
1393 }
1394 return;
1395 }
1396
1397 if (element->getType() == Element::map) {
1398 for (auto const& kv : other->mapValue()) {
1399 auto current_key = kv.first;
1400 auto value = boost::const_pointer_cast<Element>(kv.second);
1401 if (value && value->getType() != Element::null) {
1402 if (element->contains(current_key) &&
1403 (value->getType() == Element::map ||
1404 value->getType() == Element::list)) {
1405 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1406 if (container == key) {
1407 alter = true;
1408 }
1409 extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
1410 } else if (alter && current_key == extension) {
1411 element->set(current_key, value);
1412 }
1413 }
1414 }
1415 return;
1416 }
1417}
1418
1420copy(ConstElementPtr from, int level) {
1421 if (!from) {
1422 isc_throw(BadValue, "copy got a null pointer");
1423 }
1424 int from_type = from->getType();
1425 if (from_type == Element::integer) {
1426 return (ElementPtr(new IntElement(from->intValue())));
1427 } else if (from_type == Element::real) {
1428 return (ElementPtr(new DoubleElement(from->doubleValue())));
1429 } else if (from_type == Element::boolean) {
1430 return (ElementPtr(new BoolElement(from->boolValue())));
1431 } else if (from_type == Element::null) {
1432 return (ElementPtr(new NullElement()));
1433 } else if (from_type == Element::string) {
1434 return (ElementPtr(new StringElement(from->stringValue())));
1435 } else if (from_type == Element::list) {
1436 ElementPtr result = ElementPtr(new ListElement());
1437 for (auto const& elem : from->listValue()) {
1438 if (level == 0) {
1439 result->add(elem);
1440 } else {
1441 result->add(copy(elem, level - 1));
1442 }
1443 }
1444 return (result);
1445 } else if (from_type == Element::map) {
1446 ElementPtr result = ElementPtr(new MapElement());
1447 for (auto const& kv : from->mapValue()) {
1448 auto key = kv.first;
1449 auto value = kv.second;
1450 if (level == 0) {
1451 result->set(key, value);
1452 } else {
1453 result->set(key, copy(value, level - 1));
1454 }
1455 }
1456 return (result);
1457 } else {
1458 isc_throw(BadValue, "copy got an element of type: " << from_type);
1459 }
1460}
1461
1462namespace {
1463
1464// Helper function which blocks infinite recursion
1465bool
1466isEquivalent0(ConstElementPtr a, ConstElementPtr b, unsigned level) {
1467 // check looping forever on cycles
1468 if (!level) {
1469 isc_throw(BadValue, "isEquivalent got infinite recursion: "
1470 "arguments include cycles");
1471 }
1472 if (!a || !b) {
1473 isc_throw(BadValue, "isEquivalent got a null pointer");
1474 }
1475 // check types
1476 if (a->getType() != b->getType()) {
1477 return (false);
1478 }
1479 if (a->getType() == Element::list) {
1480 // check empty
1481 if (a->empty()) {
1482 return (b->empty());
1483 }
1484 // check size
1485 if (a->size() != b->size()) {
1486 return (false);
1487 }
1488
1489 // copy b into a list
1490 const size_t s = a->size();
1491 std::list<ConstElementPtr> l;
1492 for (size_t i = 0; i < s; ++i) {
1493 l.push_back(b->get(i));
1494 }
1495
1496 // iterate on a
1497 for (size_t i = 0; i < s; ++i) {
1498 ConstElementPtr item = a->get(i);
1499 // lookup this item in the list
1500 bool found = false;
1501 for (auto it = l.begin(); it != l.end(); ++it) {
1502 // if found in the list remove it
1503 if (isEquivalent0(item, *it, level - 1)) {
1504 found = true;
1505 l.erase(it);
1506 break;
1507 }
1508 }
1509 // if not found argument differs
1510 if (!found) {
1511 return (false);
1512 }
1513 }
1514
1515 // sanity check: the list must be empty
1516 if (!l.empty()) {
1517 isc_throw(Unexpected, "isEquivalent internal error");
1518 }
1519 return (true);
1520 } else if (a->getType() == Element::map) {
1521 // check sizes
1522 if (a->size() != b->size()) {
1523 return (false);
1524 }
1525 // iterate on the first map
1526 for (auto const& kv : a->mapValue()) {
1527 // get the b value for the given keyword and recurse
1528 ConstElementPtr item = b->get(kv.first);
1529 if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1530 return (false);
1531 }
1532 }
1533 return (true);
1534 } else {
1535 return (a->equals(*b));
1536 }
1537}
1538
1539} // end anonymous namespace
1540
1541bool
1543 return (isEquivalent0(a, b, 100));
1544}
1545
1546void
1547prettyPrint(ConstElementPtr element, std::ostream& out,
1548 unsigned indent, unsigned step) {
1549 if (!element) {
1550 isc_throw(BadValue, "prettyPrint got a null pointer");
1551 }
1552 if (element->getType() == Element::list) {
1553 // empty list case
1554 if (element->empty()) {
1555 out << "[ ]";
1556 return;
1557 }
1558
1559 // complex ? multiline : oneline
1560 if (!element->get(0)) {
1561 isc_throw(BadValue, "prettyPrint got a null pointer");
1562 }
1563 int first_type = element->get(0)->getType();
1564 bool complex = false;
1565 if ((first_type == Element::list) || (first_type == Element::map)) {
1566 complex = true;
1567 }
1568 std::string separator = complex ? ",\n" : ", ";
1569
1570 // open the list
1571 out << "[" << (complex ? "\n" : " ");
1572
1573 // iterate on items
1574 auto const& l = element->listValue();
1575 bool first = true;
1576 for (auto const& it : l) {
1577 // add the separator if not the first item
1578 if (!first) {
1579 out << separator;
1580 } else {
1581 first = false;
1582 }
1583 // add indentation
1584 if (complex) {
1585 out << std::string(indent + step, ' ');
1586 }
1587 // recursive call
1588 prettyPrint(it, out, indent + step, step);
1589 }
1590
1591 // close the list
1592 if (complex) {
1593 out << "\n" << std::string(indent, ' ');
1594 } else {
1595 out << " ";
1596 }
1597 out << "]";
1598 } else if (element->getType() == Element::map) {
1599 // empty map case
1600 if (element->size() == 0) {
1601 out << "{ }";
1602 return;
1603 }
1604
1605 // open the map
1606 out << "{\n";
1607
1608 // iterate on keyword: value
1609 auto const& m = element->mapValue();
1610 bool first = true;
1611 for (auto const& it : m) {
1612 // add the separator if not the first item
1613 if (first) {
1614 first = false;
1615 } else {
1616 out << ",\n";
1617 }
1618 // add indentation
1619 out << std::string(indent + step, ' ');
1620 // add keyword:
1621 out << "\"" << it.first << "\": ";
1622 // recursive call
1623 prettyPrint(it.second, out, indent + step, step);
1624 }
1625
1626 // close the map
1627 out << "\n" << std::string(indent, ' ') << "}";
1628 } else {
1629 // not a list or a map
1630 element->toJSON(out);
1631 }
1632}
1633
1634std::string
1635prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
1636 std::stringstream ss;
1637 prettyPrint(element, ss, indent, step);
1638 return (ss.str());
1639}
1640
1641void Element::preprocess(std::istream& in, std::stringstream& out) {
1642
1643 std::string line;
1644
1645 while (std::getline(in, line)) {
1646 // If this is a comments line, replace it with empty line
1647 // (so the line numbers will still match
1648 if (!line.empty() && line[0] == '#') {
1649 line = "";
1650 }
1651
1652 // getline() removes end line characters. Unfortunately, we need
1653 // it for getting the line numbers right (in case we report an
1654 // error.
1655 out << line;
1656 out << "\n";
1657 }
1658}
1659
1660} // end of isc::data namespace
1661} // end of isc namespace
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.
Wrapper over int128_t.
Definition: data.h:630
bool equals(const Element &other) const override
Checks whether the other Element is equal.
Definition: data.cc:1038
void toJSON(std::ostream &ss) const override
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:839
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:1055
bool equals(const Element &other) const
Definition: data.cc:1049
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:844
The Element class represents a piece of data, used by the command channel and configuration parts.
Definition: data.h:72
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:249
virtual bool equals(const Element &other) const =0
types
The types that an Element can hold.
Definition: data.h:139
virtual bool getValue(int64_t &t) const
Definition: data.cc:73
static std::string typeToName(Element::types type)
Returns the name of the given type as a string.
Definition: data.cc:651
virtual int64_t intValue() const
Definition: data.h:234
std::string str() const
Returns a string representing the Element and all its child elements; note that this is different fro...
Definition: data.cc:54
virtual std::string stringValue() const
Definition: data.h:243
std::string toWire() const
Returns the wireformat for the Element and all its child elements.
Definition: data.cc:61
static ElementPtr fromWire(std::stringstream &in, int length)
These function pparse the wireformat at the given stringstream (of the given length).
Definition: data.cc:992
virtual bool setValue(const long long int v)
Definition: data.cc:103
static ElementPtr fromJSONFile(const std::string &file_name, bool preproc=false)
Reads contents of specified file and interprets it as JSON.
Definition: data.cc:817
virtual bool empty() const
Return true if there are no elements in the list.
Definition: data.cc:168
virtual void remove(const int i)
Removes the element at the given position.
Definition: data.cc:158
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.
Definition: data.cc:188
virtual ConstElementPtr find(const std::string &identifier) const
Recursively finds any data at the given identifier.
Definition: data.cc:193
virtual size_t size() const
Returns the number of elements in the list.
Definition: data.cc:163
virtual const std::map< std::string, ConstElementPtr > & mapValue() const
Definition: data.h:249
virtual void add(ElementPtr element)
Adds an ElementPtr to the list.
Definition: data.cc:153
virtual const std::vector< ElementPtr > & listValue() const
Definition: data.h:245
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:798
virtual ConstElementPtr get(const int i) const
Returns the ElementPtr at the given index.
Definition: data.cc:138
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:304
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:677
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:299
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:148
virtual double doubleValue() const
Definition: data.h:239
virtual isc::util::int128_t bigIntValue() const
Definition: data.h:236
types getType() const
Definition: data.h:178
virtual bool boolValue() const
Definition: data.h:241
static void preprocess(std::istream &in, std::stringstream &out)
input text preprocessor
Definition: data.cc:1641
virtual ElementPtr getNonConst(const int i) const
returns element as non-const pointer
Definition: data.cc:143
Notes: IntElement type is changed to int64_t.
Definition: data.h:615
bool equals(const Element &other) const
Definition: data.cc:1027
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:834
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:49
void sort(std::string const &index=std::string())
Sorts the elements inside the list.
Definition: data.cc:1090
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:922
bool equals(const Element &other) const
Definition: data.cc:1072
ConstElementPtr find(const std::string &id) const override
Recursively finds any data at the given identifier.
Definition: data.cc:964
void set(const std::string &key, ConstElementPtr value) override
Sets the ElementPtr at the given key.
Definition: data.cc:1008
bool equals(const Element &other) const override
Definition: data.cc:1126
void toJSON(std::ostream &ss) const override
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:939
bool equals(const Element &other) const
Definition: data.cc:1061
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:869
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:874
bool equals(const Element &other) const
Definition: data.cc:1066
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition: data.h:36
#define throwTypeError(error)
Add the position to a TypeError message should be used in place of isc_throw(TypeError,...
Definition: data.h:205
#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:1420
bool operator==(const Element &a, const Element &b)
Definition: data.cc:218
void mergeDiffAdd(ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx)
Merges the diff data by adding the missing elements from 'other' to 'element' (recursively).
Definition: data.cc:1217
void removeIdentical(ElementPtr a, ConstElementPtr b)
Remove all values from the first ElementPtr that are equal in the second.
Definition: data.cc:1153
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1199
void mergeDiffDel(ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx)
Merges the diff data by removing the data present in 'other' from 'element' (recursively).
Definition: data.cc:1278
bool operator<(Element const &a, Element const &b)
Definition: data.cc:227
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
Definition: data.cc:1542
void prettyPrint(ConstElementPtr element, std::ostream &out, unsigned indent, unsigned step)
Pretty prints the data into stream.
Definition: data.cc:1547
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:29
void extend(const std::string &container, const std::string &extension, ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx, bool alter)
Extends data by adding the specified 'extension' elements from 'other' inside the 'container' element...
Definition: data.cc:1368
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
Definition: data.cc:1148
std::ostream & operator<<(std::ostream &out, const Element::Position &pos)
Insert Element::Position as a string into stream.
Definition: data.cc:48
bool operator!=(const Element &a, const Element &b)
Definition: data.cc:222
boost::shared_ptr< Element > ElementPtr
Definition: data.h:28
std::vector< FunctionMap > HierarchyDescriptor
Hierarchy descriptor of the containers in a specific Element hierarchy tree.
Definition: data.h:924
boost::multiprecision::checked_int128_t int128_t
Definition: bigints.h:19
Defines the logger used by the top-level component of kea-lfc.
Represents the position of the data element within a configuration string.
Definition: data.h:94
uint32_t pos_
Position within the line.
Definition: data.h:97
std::string str() const
Returns the position in the textual format.
Definition: data.cc:41
uint32_t line_
Line number.
Definition: data.h:96
std::string file_
File name.
Definition: data.h:95