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