Kea 2.5.5
data.cc
Go to the documentation of this file.
1// Copyright (C) 2010-2023 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 for (auto it = v.begin(); it != v.end(); ++it) {
927 if (it != v.begin()) {
928 ss << ", ";
929 }
930 (*it)->toJSON(ss);
931 }
932 ss << " ]";
933}
934
935void
936MapElement::toJSON(std::ostream& ss) const {
937 ss << "{ ";
938
939 for (auto it = m.begin(); it != m.end(); ++it) {
940 if (it != m.begin()) {
941 ss << ", ";
942 }
943 ss << "\"" << (*it).first << "\": ";
944 if ((*it).second) {
945 (*it).second->toJSON(ss);
946 } else {
947 ss << "None";
948 }
949 }
950 ss << " }";
951}
952
953// throws when one of the types in the path (except the one
954// we're looking for) is not a MapElement
955// returns 0 if it could simply not be found
956// should that also be an exception?
958MapElement::find(const std::string& id) const {
959 const size_t sep = id.find('/');
960 if (sep == std::string::npos) {
961 return (get(id));
962 } else {
963 ConstElementPtr ce = get(id.substr(0, sep));
964 if (ce) {
965 // ignore trailing slash
966 if (sep + 1 != id.size()) {
967 return (ce->find(id.substr(sep + 1)));
968 } else {
969 return (ce);
970 }
971 } else {
972 return (ElementPtr());
973 }
974 }
975}
976
978Element::fromWire(const std::string& s) {
979 std::stringstream ss;
980 ss << s;
981 int line = 0, pos = 0;
982 return (fromJSON(ss, "<wire>", line, pos));
983}
984
986Element::fromWire(std::stringstream& in, int) {
987 //
988 // Check protocol version
989 //
990 //for (int i = 0 ; i < 4 ; ++i) {
991 // const unsigned char version_byte = get_byte(in);
992 // if (PROTOCOL_VERSION[i] != version_byte) {
993 // throw DecodeError("Protocol version incorrect");
994 // }
995 //}
996 //length -= 4;
997 int line = 0, pos = 0;
998 return (fromJSON(in, "<wire>", line, pos));
999}
1000
1001void
1002MapElement::set(const std::string& key, ConstElementPtr value) {
1003 m[key] = value;
1004}
1005
1006bool
1007MapElement::find(const std::string& id, ConstElementPtr& t) const {
1008 try {
1009 ConstElementPtr p = find(id);
1010 if (p) {
1011 t = p;
1012 return (true);
1013 }
1014 } catch (const TypeError&) {
1015 // ignore
1016 }
1017 return (false);
1018}
1019
1020bool
1021IntElement::equals(const Element& other) const {
1022 // Let's not be very picky with constraining the integer types to be the
1023 // same. Equality is sometimes checked from high-up in the Element hierarcy.
1024 // That is a context which, most of the time, does not have information on
1025 // the type of integers stored on Elements lower in the hierarchy. So it
1026 // would be difficult to differentiate between the integer types.
1027 return (other.getType() == Element::integer && i == other.intValue()) ||
1028 (other.getType() == Element::bigint && i == other.bigIntValue());
1029}
1030
1031bool
1032BigIntElement::equals(const Element& other) const {
1033 // Let's not be very picky with constraining the integer types to be the
1034 // same. Equality is sometimes checked from high-up in the Element hierarcy.
1035 // That is a context which, most of the time, does not have information on
1036 // the type of integers stored on Elements lower in the hierarchy. So it
1037 // would be difficult to differentiate between the integer types.
1038 return (other.getType() == Element::bigint && i_ == other.bigIntValue()) ||
1039 (other.getType() == Element::integer && i_ == other.intValue());
1040}
1041
1042bool
1043DoubleElement::equals(const Element& other) const {
1044 return (other.getType() == Element::real) &&
1045 (fabs(d - other.doubleValue()) < 1e-14);
1046}
1047
1048bool
1049BoolElement::equals(const Element& other) const {
1050 return (other.getType() == Element::boolean) &&
1051 (b == other.boolValue());
1052}
1053
1054bool
1055NullElement::equals(const Element& other) const {
1056 return (other.getType() == Element::null);
1057}
1058
1059bool
1060StringElement::equals(const Element& other) const {
1061 return (other.getType() == Element::string) &&
1062 (s == other.stringValue());
1063}
1064
1065bool
1066ListElement::equals(const Element& other) const {
1067 if (other.getType() == Element::list) {
1068 const size_t s = size();
1069 if (s != other.size()) {
1070 return (false);
1071 }
1072 for (size_t i = 0; i < s; ++i) {
1073 if (!get(i)->equals(*other.get(i))) {
1074 return (false);
1075 }
1076 }
1077 return (true);
1078 } else {
1079 return (false);
1080 }
1081}
1082
1083void
1084ListElement::sort(std::string const& index /* = std::string() */) {
1085 if (l.empty()) {
1086 return;
1087 }
1088
1089 int const t(l.at(0)->getType());
1090 std::function<bool(ElementPtr, ElementPtr)> comparator;
1091 if (t == map) {
1092 if (index.empty()) {
1093 isc_throw(BadValue, "index required when sorting maps");
1094 }
1095 comparator = [&](ElementPtr const& a, ElementPtr const& b) {
1096 ConstElementPtr const& ai(a->get(index));
1097 ConstElementPtr const& bi(b->get(index));
1098 if (ai && bi) {
1099 return *ai < *bi;
1100 }
1101 return true;
1102 };
1103 } else if (t == list) {
1104 // Nested lists. Not supported.
1105 return;
1106 } else {
1107 // Assume scalars.
1108 if (!index.empty()) {
1109 isc_throw(BadValue, "index given when sorting scalars?");
1110 }
1111 comparator = [&](ElementPtr const& a, ElementPtr const& b) {
1112 return *a < *b;
1113 };
1114 }
1115
1116 std::sort(l.begin(), l.end(), comparator);
1117}
1118
1119bool
1120MapElement::equals(const Element& other) const {
1121 if (other.getType() == Element::map) {
1122 if (size() != other.size()) {
1123 return (false);
1124 }
1125 for (auto kv : mapValue()) {
1126 auto key = kv.first;
1127 if (other.contains(key)) {
1128 if (!get(key)->equals(*other.get(key))) {
1129 return (false);
1130 }
1131 } else {
1132 return (false);
1133 }
1134 }
1135 return (true);
1136 } else {
1137 return (false);
1138 }
1139}
1140
1141bool
1143 return (!p);
1144}
1145
1146void
1148 if (!b) {
1149 return;
1150 }
1151 if (a->getType() != Element::map || b->getType() != Element::map) {
1152 isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1153 }
1154
1155 // As maps do not allow entries with multiple keys, we can either iterate
1156 // over a checking for identical entries in b or vice-versa. As elements
1157 // are removed from a if a match is found, we choose to iterate over b to
1158 // avoid problems with element removal affecting the iterator.
1159 for (auto kv : b->mapValue()) {
1160 auto key = kv.first;
1161 if (a->contains(key)) {
1162 if (a->get(key)->equals(*b->get(key))) {
1163 a->remove(key);
1164 }
1165 }
1166 }
1167}
1168
1171 ElementPtr result = Element::createMap();
1172
1173 if (!b) {
1174 return (result);
1175 }
1176
1177 if (a->getType() != Element::map || b->getType() != Element::map) {
1178 isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1179 }
1180
1181 for (auto kv : a->mapValue()) {
1182 auto key = kv.first;
1183 if (!b->contains(key) ||
1184 !a->get(key)->equals(*b->get(key))) {
1185 result->set(key, kv.second);
1186 }
1187 }
1188
1189 return (result);
1190}
1191
1192void
1194 if (element->getType() != Element::map ||
1195 other->getType() != Element::map) {
1196 isc_throw(TypeError, "merge arguments not MapElements");
1197 }
1198
1199 for (auto kv : other->mapValue()) {
1200 auto key = kv.first;
1201 auto value = kv.second;
1202 if (value && value->getType() != Element::null) {
1203 element->set(key, value);
1204 } else if (element->contains(key)) {
1205 element->remove(key);
1206 }
1207 }
1208}
1209
1210void
1212 HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
1213 if (element->getType() != other->getType()) {
1214 isc_throw(TypeError, "mergeDiffAdd arguments not same type");
1215 }
1216
1217 if (element->getType() == Element::list) {
1218 // Store new elements in a separate container so we don't overwrite
1219 // options as we add them (if there are duplicates).
1220 ElementPtr new_elements = Element::createList();
1221 for (auto& right : other->listValue()) {
1222 // Check if we have any description of the key in the configuration
1223 // hierarchy.
1224 auto f = hierarchy[idx].find(key);
1225 if (f != hierarchy[idx].end()) {
1226 bool found = false;
1227 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1228 for (auto& left : element->listValue()) {
1229 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1230 // Check if the elements refer to the same configuration
1231 // entity.
1232 if (f->second.match_(mutable_left, mutable_right)) {
1233 found = true;
1234 mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
1235 }
1236 }
1237 if (!found) {
1238 new_elements->add(right);
1239 }
1240 } else {
1241 new_elements->add(right);
1242 }
1243 }
1244 // Finally add the new elements.
1245 for (auto& right : new_elements->listValue()) {
1246 element->add(right);
1247 }
1248 return;
1249 }
1250
1251 if (element->getType() == Element::map) {
1252 for (auto kv : other->mapValue()) {
1253 auto current_key = kv.first;
1254 auto value = boost::const_pointer_cast<Element>(kv.second);
1255 if (value && value->getType() != Element::null) {
1256 if (element->contains(current_key) &&
1257 (value->getType() == Element::map ||
1258 value->getType() == Element::list)) {
1259 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1260 mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
1261 } else {
1262 element->set(current_key, value);
1263 }
1264 }
1265 }
1266 return;
1267 }
1268 element = other;
1269}
1270
1271void
1273 HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
1274 if (element->getType() != other->getType()) {
1275 isc_throw(TypeError, "mergeDiffDel arguments not same type");
1276 }
1277
1278 if (element->getType() == Element::list) {
1279 for (auto const& value : other->listValue()) {
1280 ElementPtr mutable_right = boost::const_pointer_cast<Element>(value);
1281 for (uint32_t iter = 0; iter < element->listValue().size();) {
1282 bool removed = false;
1283 // Check if we have any description of the key in the
1284 // configuration hierarchy.
1285 auto f = hierarchy[idx].find(key);
1286 if (f != hierarchy[idx].end()) {
1287 ElementPtr mutable_left = boost::const_pointer_cast<Element>(element->listValue().at(iter));
1288 // Check if the elements refer to the same configuration
1289 // entity.
1290 if (f->second.match_(mutable_left, mutable_right)) {
1291 // Check if the user supplied data only contains
1292 // identification information, so the intent is to
1293 // delete the element, not just element data.
1294 if (f->second.no_data_(mutable_right)) {
1295 element->remove(iter);
1296 removed = true;
1297 } else {
1298 mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
1299 if (mutable_left->empty()) {
1300 element->remove(iter);
1301 removed = true;
1302 }
1303 }
1304 }
1305 } else if (element->listValue().at(iter)->equals(*value)) {
1306 element->remove(iter);
1307 removed = true;
1308 }
1309 if (!removed) {
1310 ++iter;
1311 }
1312 }
1313 }
1314 return;
1315 }
1316
1317 if (element->getType() == Element::map) {
1318 // If the resulting element still contains data, we need to restore the
1319 // key parameters, so we store them here.
1320 ElementPtr new_elements = Element::createMap();
1321 for (auto kv : other->mapValue()) {
1322 auto current_key = kv.first;
1323 auto value = boost::const_pointer_cast<Element>(kv.second);
1324 if (value && value->getType() != Element::null) {
1325 if (element->contains(current_key)) {
1326 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1327 if (mutable_element->getType() == Element::map ||
1328 mutable_element->getType() == Element::list) {
1329 mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
1330 if (mutable_element->empty()) {
1331 element->remove(current_key);
1332 }
1333 } else {
1334 // Check if we have any description of the key in the
1335 // configuration hierarchy.
1336 auto f = hierarchy[idx].find(key);
1337 if (f != hierarchy[idx].end()) {
1338 // Check if the key is used for element
1339 // identification.
1340 if (f->second.is_key_(current_key)) {
1341 // Store the key parameter.
1342 new_elements->set(current_key, mutable_element);
1343 }
1344 }
1345 element->remove(current_key);
1346 }
1347 }
1348 }
1349 }
1350 // If the element still contains data, restore the key elements.
1351 if (element->size()) {
1352 for (auto kv : new_elements->mapValue()) {
1353 element->set(kv.first, kv.second);
1354 }
1355 }
1356 return;
1357 }
1358 element = ElementPtr(new NullElement);
1359}
1360
1361void
1362extend(const std::string& container, const std::string& extension,
1363 ElementPtr& element, ElementPtr& other, HierarchyDescriptor& hierarchy,
1364 std::string key, size_t idx, bool alter) {
1365 if (element->getType() != other->getType()) {
1366 isc_throw(TypeError, "extend arguments not same type");
1367 }
1368
1369 if (element->getType() == Element::list) {
1370 for (auto& right : other->listValue()) {
1371 // Check if we have any description of the key in the configuration
1372 // hierarchy.
1373 auto f = hierarchy[idx].find(key);
1374 if (f != hierarchy[idx].end()) {
1375 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1376 for (auto& left : element->listValue()) {
1377 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1378 if (container == key) {
1379 alter = true;
1380 }
1381 if (f->second.match_(mutable_left, mutable_right)) {
1382 extend(container, extension, mutable_left, mutable_right,
1383 hierarchy, key, idx, alter);
1384 }
1385 }
1386 }
1387 }
1388 return;
1389 }
1390
1391 if (element->getType() == Element::map) {
1392 for (auto kv : other->mapValue()) {
1393 auto current_key = kv.first;
1394 auto value = boost::const_pointer_cast<Element>(kv.second);
1395 if (value && value->getType() != Element::null) {
1396 if (element->contains(current_key) &&
1397 (value->getType() == Element::map ||
1398 value->getType() == Element::list)) {
1399 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1400 if (container == key) {
1401 alter = true;
1402 }
1403 extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
1404 } else if (alter && current_key == extension) {
1405 element->set(current_key, value);
1406 }
1407 }
1408 }
1409 return;
1410 }
1411}
1412
1414copy(ConstElementPtr from, int level) {
1415 if (!from) {
1416 isc_throw(BadValue, "copy got a null pointer");
1417 }
1418 int from_type = from->getType();
1419 if (from_type == Element::integer) {
1420 return (ElementPtr(new IntElement(from->intValue())));
1421 } else if (from_type == Element::real) {
1422 return (ElementPtr(new DoubleElement(from->doubleValue())));
1423 } else if (from_type == Element::boolean) {
1424 return (ElementPtr(new BoolElement(from->boolValue())));
1425 } else if (from_type == Element::null) {
1426 return (ElementPtr(new NullElement()));
1427 } else if (from_type == Element::string) {
1428 return (ElementPtr(new StringElement(from->stringValue())));
1429 } else if (from_type == Element::list) {
1430 ElementPtr result = ElementPtr(new ListElement());
1431 for (auto elem : from->listValue()) {
1432 if (level == 0) {
1433 result->add(elem);
1434 } else {
1435 result->add(copy(elem, level - 1));
1436 }
1437 }
1438 return (result);
1439 } else if (from_type == Element::map) {
1440 ElementPtr result = ElementPtr(new MapElement());
1441 for (auto kv : from->mapValue()) {
1442 auto key = kv.first;
1443 auto value = kv.second;
1444 if (level == 0) {
1445 result->set(key, value);
1446 } else {
1447 result->set(key, copy(value, level - 1));
1448 }
1449 }
1450 return (result);
1451 } else {
1452 isc_throw(BadValue, "copy got an element of type: " << from_type);
1453 }
1454}
1455
1456namespace {
1457
1458// Helper function which blocks infinite recursion
1459bool
1460isEquivalent0(ConstElementPtr a, ConstElementPtr b, unsigned level) {
1461 // check looping forever on cycles
1462 if (!level) {
1463 isc_throw(BadValue, "isEquivalent got infinite recursion: "
1464 "arguments include cycles");
1465 }
1466 if (!a || !b) {
1467 isc_throw(BadValue, "isEquivalent got a null pointer");
1468 }
1469 // check types
1470 if (a->getType() != b->getType()) {
1471 return (false);
1472 }
1473 if (a->getType() == Element::list) {
1474 // check empty
1475 if (a->empty()) {
1476 return (b->empty());
1477 }
1478 // check size
1479 if (a->size() != b->size()) {
1480 return (false);
1481 }
1482
1483 // copy b into a list
1484 const size_t s = a->size();
1485 std::list<ConstElementPtr> l;
1486 for (size_t i = 0; i < s; ++i) {
1487 l.push_back(b->get(i));
1488 }
1489
1490 // iterate on a
1491 for (size_t i = 0; i < s; ++i) {
1492 ConstElementPtr item = a->get(i);
1493 // lookup this item in the list
1494 bool found = false;
1495 for (auto it = l.begin(); it != l.end(); ++it) {
1496 // if found in the list remove it
1497 if (isEquivalent0(item, *it, level - 1)) {
1498 found = true;
1499 l.erase(it);
1500 break;
1501 }
1502 }
1503 // if not found argument differs
1504 if (!found) {
1505 return (false);
1506 }
1507 }
1508
1509 // sanity check: the list must be empty
1510 if (!l.empty()) {
1511 isc_throw(Unexpected, "isEquivalent internal error");
1512 }
1513 return (true);
1514 } else if (a->getType() == Element::map) {
1515 // check sizes
1516 if (a->size() != b->size()) {
1517 return (false);
1518 }
1519 // iterate on the first map
1520 for (auto kv : a->mapValue()) {
1521 // get the b value for the given keyword and recurse
1522 ConstElementPtr item = b->get(kv.first);
1523 if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1524 return (false);
1525 }
1526 }
1527 return (true);
1528 } else {
1529 return (a->equals(*b));
1530 }
1531}
1532
1533} // end anonymous namespace
1534
1535bool
1537 return (isEquivalent0(a, b, 100));
1538}
1539
1540void
1541prettyPrint(ConstElementPtr element, std::ostream& out,
1542 unsigned indent, unsigned step) {
1543 if (!element) {
1544 isc_throw(BadValue, "prettyPrint got a null pointer");
1545 }
1546 if (element->getType() == Element::list) {
1547 // empty list case
1548 if (element->empty()) {
1549 out << "[ ]";
1550 return;
1551 }
1552
1553 // complex ? multiline : oneline
1554 if (!element->get(0)) {
1555 isc_throw(BadValue, "prettyPrint got a null pointer");
1556 }
1557 int first_type = element->get(0)->getType();
1558 bool complex = false;
1559 if ((first_type == Element::list) || (first_type == Element::map)) {
1560 complex = true;
1561 }
1562 std::string separator = complex ? ",\n" : ", ";
1563
1564 // open the list
1565 out << "[" << (complex ? "\n" : " ");
1566
1567 // iterate on items
1568 const auto& l = element->listValue();
1569 for (auto it = l.begin(); it != l.end(); ++it) {
1570 // add the separator if not the first item
1571 if (it != l.begin()) {
1572 out << separator;
1573 }
1574 // add indentation
1575 if (complex) {
1576 out << std::string(indent + step, ' ');
1577 }
1578 // recursive call
1579 prettyPrint(*it, out, indent + step, step);
1580 }
1581
1582 // close the list
1583 if (complex) {
1584 out << "\n" << std::string(indent, ' ');
1585 } else {
1586 out << " ";
1587 }
1588 out << "]";
1589 } else if (element->getType() == Element::map) {
1590 // empty map case
1591 if (element->size() == 0) {
1592 out << "{ }";
1593 return;
1594 }
1595
1596 // open the map
1597 out << "{\n";
1598
1599 // iterate on keyword: value
1600 const auto& m = element->mapValue();
1601 bool first = true;
1602 for (auto it = m.begin(); it != m.end(); ++it) {
1603 // add the separator if not the first item
1604 if (first) {
1605 first = false;
1606 } else {
1607 out << ",\n";
1608 }
1609 // add indentation
1610 out << std::string(indent + step, ' ');
1611 // add keyword:
1612 out << "\"" << it->first << "\": ";
1613 // recursive call
1614 prettyPrint(it->second, out, indent + step, step);
1615 }
1616
1617 // close the map
1618 out << "\n" << std::string(indent, ' ') << "}";
1619 } else {
1620 // not a list or a map
1621 element->toJSON(out);
1622 }
1623}
1624
1625std::string
1626prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
1627 std::stringstream ss;
1628 prettyPrint(element, ss, indent, step);
1629 return (ss.str());
1630}
1631
1632void Element::preprocess(std::istream& in, std::stringstream& out) {
1633
1634 std::string line;
1635
1636 while (std::getline(in, line)) {
1637 // If this is a comments line, replace it with empty line
1638 // (so the line numbers will still match
1639 if (!line.empty() && line[0] == '#') {
1640 line = "";
1641 }
1642
1643 // getline() removes end line characters. Unfortunately, we need
1644 // it for getting the line numbers right (in case we report an
1645 // error.
1646 out << line;
1647 out << "\n";
1648 }
1649}
1650
1651} // end of isc::data namespace
1652} // 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:1032
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:1049
bool equals(const Element &other) const
Definition: data.cc:1043
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:986
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:1632
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:1021
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:1084
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:1066
ConstElementPtr find(const std::string &id) const override
Recursively finds any data at the given identifier.
Definition: data.cc:958
void set(const std::string &key, ConstElementPtr value) override
Sets the ElementPtr at the given key.
Definition: data.cc:1002
bool equals(const Element &other) const override
Definition: data.cc:1120
void toJSON(std::ostream &ss) const override
Converts the Element to JSON format and appends it to the given stringstream.
Definition: data.cc:936
bool equals(const Element &other) const
Definition: data.cc:1055
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:1060
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:1414
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:1211
void removeIdentical(ElementPtr a, ConstElementPtr b)
Remove all values from the first ElementPtr that are equal in the second.
Definition: data.cc:1147
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1193
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:1272
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:1536
void prettyPrint(ConstElementPtr element, std::ostream &out, unsigned indent, unsigned step)
Pretty prints the data into stream.
Definition: data.cc:1541
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:1362
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
Definition: data.cc:1142
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
@ error
Definition: db_log.h:116
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