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