Kea  2.3.2-git
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 
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(const double) {
106  return (false);
107 }
108 
109 bool
110 Element::setValue(const bool) {
111  return (false);
112 }
113 
114 bool
115 Element::setValue(const std::string&) {
116  return (false);
117 }
118 
119 bool
120 Element::setValue(const std::vector<ElementPtr>&) {
121  return (false);
122 }
123 
124 bool
125 Element::setValue(const std::map<std::string, ConstElementPtr>&) {
126  return (false);
127 }
128 
130 Element::get(const int) const {
131  throwTypeError("get(int) called on a non-container Element");
132 }
133 
135 Element::getNonConst(const int) const {
136  throwTypeError("get(int) called on a non-container Element");
137 }
138 
139 void
140 Element::set(const size_t, ElementPtr) {
141  throwTypeError("set(int, element) called on a non-list Element");
142 }
143 
144 void
145 Element::add(ElementPtr) {
146  throwTypeError("add() called on a non-list Element");
147 }
148 
149 void
150 Element::remove(const int) {
151  throwTypeError("remove(int) called on a non-container Element");
152 }
153 
154 size_t
155 Element::size() const {
156  throwTypeError("size() called on a non-list Element");
157 }
158 
159 bool
160 Element::empty() const {
161  throwTypeError("empty() called on a non-container Element");
162 }
163 
165 Element::get(const std::string&) const {
166  throwTypeError("get(string) called on a non-map Element");
167 }
168 
169 void
170 Element::set(const std::string&, ConstElementPtr) {
171  throwTypeError("set(name, element) called on a non-map Element");
172 }
173 
174 void
175 Element::remove(const std::string&) {
176  throwTypeError("remove(string) called on a non-map Element");
177 }
178 
179 bool
180 Element::contains(const std::string&) const {
181  throwTypeError("contains(string) called on a non-map Element");
182 }
183 
185 Element::find(const std::string&) const {
186  throwTypeError("find(string) called on a non-map Element");
187 }
188 
189 bool
190 Element::find(const std::string&, ConstElementPtr&) const {
191  return (false);
192 }
193 
194 namespace {
195 inline void
196 throwJSONError(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 
204 std::ostream&
205 operator<<(std::ostream& out, const Element& e) {
206  return (out << e.str());
207 }
208 
209 bool
210 operator==(const Element& a, const Element& b) {
211  return (a.equals(b));
212 }
213 
214 bool operator!=(const Element& a, const Element& b) {
215  return (!a.equals(b));
216 }
217 
218 bool
219 operator<(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 //
241 Element::create(const Position& pos) {
242  return (ElementPtr(new NullElement(pos)));
243 }
244 
246 Element::create(const long long int i, const Position& pos) {
247  return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
248 }
249 
251 Element::create(const int i, const Position& pos) {
252  return (create(static_cast<long long int>(i), pos));
253 }
254 
256 Element::create(const long int i, const Position& pos) {
257  return (create(static_cast<long long int>(i), pos));
258 }
259 
261 Element::create(const uint32_t i, const Position& pos) {
262  return (create(static_cast<long long int>(i), pos));
263 }
264 
266 Element::create(const double d, const Position& pos) {
267  return (ElementPtr(new DoubleElement(d, pos)));
268 }
269 
271 Element::create(const bool b, const Position& pos) {
272  return (ElementPtr(new BoolElement(b, pos)));
273 }
274 
276 Element::create(const std::string& s, const Position& pos) {
277  return (ElementPtr(new StringElement(s, pos)));
278 }
279 
281 Element::create(const char *s, const Position& pos) {
282  return (create(std::string(s), pos));
283 }
284 
286 Element::createList(const Position& pos) {
287  return (ElementPtr(new ListElement(pos)));
288 }
289 
291 Element::createMap(const Position& pos) {
292  return (ElementPtr(new MapElement(pos)));
293 }
294 
295 
296 //
297 // helper functions for fromJSON factory
298 //
299 namespace {
300 bool
301 charIn(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 
311 void
312 skipChars(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  }
324 }
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).
331 int
332 skipTo(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)?
365 std::string
366 strFromStringstream(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 
466 std::string
467 wordFromStringstream(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 
476 std::string
477 numberFromStringstream(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 //
492 fromStringstreamNumber(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 
521 fromStringstreamBool(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 
543 fromStringstreamNull(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 
560 fromStringstreamString(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 
572 fromStringstreamList(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 
593 fromStringstreamMap(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 
620 std::string
621 Element::typeToName(Element::types type) {
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 
645 Element::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 
670 Element::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 
684 Element::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 
694 Element::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 
764 Element::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 
783 Element::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 
799 void
800 IntElement::toJSON(std::ostream& ss) const {
801  ss << intValue();
802 }
803 
804 void
805 DoubleElement::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 
820 void
821 BoolElement::toJSON(std::ostream& ss) const {
822  if (boolValue()) {
823  ss << "true";
824  } else {
825  ss << "false";
826  }
827 }
828 
829 void
830 NullElement::toJSON(std::ostream& ss) const {
831  ss << "null";
832 }
833 
834 void
835 StringElement::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 
882 void
883 ListElement::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 
896 void
897 MapElement::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?
920 MapElement::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 
940 Element::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 
948 Element::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 
963 void
964 MapElement::set(const std::string& key, ConstElementPtr value) {
965  m[key] = value;
966 }
967 
968 bool
969 MapElement::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 
982 bool
983 IntElement::equals(const Element& other) const {
984  return (other.getType() == Element::integer) &&
985  (i == other.intValue());
986 }
987 
988 bool
989 DoubleElement::equals(const Element& other) const {
990  return (other.getType() == Element::real) &&
991  (fabs(d - other.doubleValue()) < 1e-14);
992 }
993 
994 bool
995 BoolElement::equals(const Element& other) const {
996  return (other.getType() == Element::boolean) &&
997  (b == other.boolValue());
998 }
999 
1000 bool
1001 NullElement::equals(const Element& other) const {
1002  return (other.getType() == Element::null);
1003 }
1004 
1005 bool
1006 StringElement::equals(const Element& other) const {
1007  return (other.getType() == Element::string) &&
1008  (s == other.stringValue());
1009 }
1010 
1011 bool
1012 ListElement::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 
1029 void
1030 ListElement::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 
1065 bool
1066 MapElement::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 
1087 bool
1089  return (!p);
1090 }
1091 
1092 void
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 
1138 void
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 
1156 void
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 
1217 void
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 
1307 void
1308 extend(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 
1359 ElementPtr
1360 copy(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 
1402 namespace {
1403 
1404 // Helper function which blocks infinite recursion
1405 bool
1406 isEquivalent0(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 
1481 bool
1483  return (isEquivalent0(a, b, 100));
1484 }
1485 
1486 void
1487 prettyPrint(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 
1571 std::string
1572 prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
1573  std::stringstream ss;
1574  prettyPrint(element, ss, indent, step);
1575  return (ss.str());
1576 }
1577 
1578 void 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
virtual std::string stringValue() const
Definition: data.h:221
bool operator<(Element const &a, Element const &b)
Definition: data.cc:219
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 &#39;extension&#39; elements from &#39;other&#39; inside the &#39;container&#39; element...
Definition: data.cc:1308
std::string prettyPrint(ConstElementPtr element, unsigned indent, unsigned step)
Pretty prints the data into string.
Definition: data.cc:1572
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:47
bool operator!=(const Element &a, const Element &b)
Definition: data.cc:214
virtual int64_t intValue() const
Definition: data.h:215
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
Definition: data.cc:1482
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
STL namespace.
#define throwTypeError(error)
Add the position to a TypeError message should be used in place of isc_throw(TypeError, error)
Definition: data.h:187
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
Definition: data.cc:1088
virtual bool equals(const Element &other) const =0
void mergeDiffDel(ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx)
Merges the diff data by removing the data present in &#39;other&#39; from &#39;element&#39; (recursively).
Definition: data.cc:1218
std::vector< FunctionMap > HierarchyDescriptor
Hierarchy descriptor of the containers in a specific Element hierarchy tree.
Definition: data.h:857
bool operator==(const Element &a, const Element &b)
Definition: data.cc:210
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
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
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1360
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:1116
A generic exception that is thrown when an unexpected error condition occurs.
Notes: IntElement type is changed to int64_t.
Definition: data.h:590
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
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
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition: data.h:34
Represents the position of the data element within a configuration string.
Definition: data.h:92
Defines the logger used by the top-level component of kea-lfc.
std::ostream & operator<<(std::ostream &out, const Element &e)
Insert the Element as a string into stream.
Definition: data.cc:205
std::string str() const
Returns the position in the textual format.
Definition: data.cc:38
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1139
virtual double doubleValue() const
Definition: data.h:217
int getType() const
Definition: data.h:160
A generic exception that is thrown if a function is called in a prohibited way.
virtual ConstElementPtr get(const int i) const
Returns the ElementPtr at the given index.
Definition: data.cc:130
The Element class represents a piece of data, used by the command channel and configuration parts...
Definition: data.h:70
uint16_t pos_
The position (offset from the beginning) in the buffer where the name starts.
virtual void remove(const int i)
Removes the element at the given position.
Definition: data.cc:150
void mergeDiffAdd(ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx)
Merges the diff data by adding the missing elements from &#39;other&#39; to &#39;element&#39; (recursively).
Definition: data.cc:1157
virtual bool boolValue() const
Definition: data.h:219
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.
Definition: data.cc:180