Kea  1.9.9-git
data.cc
Go to the documentation of this file.
1 // Copyright (C) 2010-2020 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-list Element");
132 }
133 
135 Element::getNonConst(const int) const {
136  throwTypeError("get(int) called on a non-list 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-list 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-list 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 //
219 // factory functions
220 //
222 Element::create(const Position& pos) {
223  return (ElementPtr(new NullElement(pos)));
224 }
225 
227 Element::create(const long long int i, const Position& pos) {
228  return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
229 }
230 
232 Element::create(const int i, const Position& pos) {
233  return (create(static_cast<long long int>(i), pos));
234 }
235 
237 Element::create(const long int i, const Position& pos) {
238  return (create(static_cast<long long int>(i), pos));
239 }
240 
242 Element::create(const double d, const Position& pos) {
243  return (ElementPtr(new DoubleElement(d, pos)));
244 }
245 
247 Element::create(const bool b, const Position& pos) {
248  return (ElementPtr(new BoolElement(b, pos)));
249 }
250 
252 Element::create(const std::string& s, const Position& pos) {
253  return (ElementPtr(new StringElement(s, pos)));
254 }
255 
257 Element::create(const char *s, const Position& pos) {
258  return (create(std::string(s), pos));
259 }
260 
262 Element::createList(const Position& pos) {
263  return (ElementPtr(new ListElement(pos)));
264 }
265 
267 Element::createMap(const Position& pos) {
268  return (ElementPtr(new MapElement(pos)));
269 }
270 
271 
272 //
273 // helper functions for fromJSON factory
274 //
275 namespace {
276 bool
277 charIn(const int c, const char* chars) {
278  const size_t chars_len = std::strlen(chars);
279  for (size_t i = 0; i < chars_len; ++i) {
280  if (chars[i] == c) {
281  return (true);
282  }
283  }
284  return (false);
285 }
286 
287 void
288 skipChars(std::istream& in, const char* chars, int& line, int& pos) {
289  int c = in.peek();
290  while (charIn(c, chars) && c != EOF) {
291  if (c == '\n') {
292  ++line;
293  pos = 1;
294  } else {
295  ++pos;
296  }
297  in.ignore();
298  c = in.peek();
299  }
300 }
301 
302 // skip on the input stream to one of the characters in chars
303 // if another character is found this function throws JSONError
304 // unless that character is specified in the optional may_skip
305 //
306 // It returns the found character (as an int value).
307 int
308 skipTo(std::istream& in, const std::string& file, int& line, int& pos,
309  const char* chars, const char* may_skip="") {
310  int c = in.get();
311  ++pos;
312  while (c != EOF) {
313  if (c == '\n') {
314  pos = 1;
315  ++line;
316  }
317  if (charIn(c, may_skip)) {
318  c = in.get();
319  ++pos;
320  } else if (charIn(c, chars)) {
321  while (charIn(in.peek(), may_skip)) {
322  if (in.peek() == '\n') {
323  pos = 1;
324  ++line;
325  } else {
326  ++pos;
327  }
328  in.ignore();
329  }
330  return (c);
331  } else {
332  throwJSONError(std::string("'") + std::string(1, c) + "' read, one of \"" + chars + "\" expected", file, line, pos);
333  }
334  }
335  throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
336  return (c); // shouldn't reach here, but some compilers require it
337 }
338 
339 // TODO: Should we check for all other official escapes here (and
340 // error on the rest)?
341 std::string
342 strFromStringstream(std::istream& in, const std::string& file,
343  const int line, int& pos) {
344  std::stringstream ss;
345  int c = in.get();
346  ++pos;
347  if (c == '"') {
348  c = in.get();
349  ++pos;
350  } else {
351  throwJSONError("String expected", file, line, pos);
352  }
353 
354  while (c != EOF && c != '"') {
355  if (c == '\\') {
356  // see the spec for allowed escape characters
357  int d;
358  switch (in.peek()) {
359  case '"':
360  c = '"';
361  break;
362  case '/':
363  c = '/';
364  break;
365  case '\\':
366  c = '\\';
367  break;
368  case 'b':
369  c = '\b';
370  break;
371  case 'f':
372  c = '\f';
373  break;
374  case 'n':
375  c = '\n';
376  break;
377  case 'r':
378  c = '\r';
379  break;
380  case 't':
381  c = '\t';
382  break;
383  case 'u':
384  // skip first 0
385  in.ignore();
386  ++pos;
387  c = in.peek();
388  if (c != '0') {
389  throwJSONError("Unsupported unicode escape", file, line, pos);
390  }
391  // skip second 0
392  in.ignore();
393  ++pos;
394  c = in.peek();
395  if (c != '0') {
396  throwJSONError("Unsupported unicode escape", file, line, pos - 2);
397  }
398  // get first digit
399  in.ignore();
400  ++pos;
401  d = in.peek();
402  if ((d >= '0') && (d <= '9')) {
403  c = (d - '0') << 4;
404  } else if ((d >= 'A') && (d <= 'F')) {
405  c = (d - 'A' + 10) << 4;
406  } else if ((d >= 'a') && (d <= 'f')) {
407  c = (d - 'a' + 10) << 4;
408  } else {
409  throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 3);
410  }
411  // get second digit
412  in.ignore();
413  ++pos;
414  d = in.peek();
415  if ((d >= '0') && (d <= '9')) {
416  c |= d - '0';
417  } else if ((d >= 'A') && (d <= 'F')) {
418  c |= d - 'A' + 10;
419  } else if ((d >= 'a') && (d <= 'f')) {
420  c |= d - 'a' + 10;
421  } else {
422  throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 4);
423  }
424  break;
425  default:
426  throwJSONError("Bad escape", file, line, pos);
427  }
428  // drop the escaped char
429  in.ignore();
430  ++pos;
431  }
432  ss.put(c);
433  c = in.get();
434  ++pos;
435  }
436  if (c == EOF) {
437  throwJSONError("Unterminated string", file, line, pos);
438  }
439  return (ss.str());
440 }
441 
442 std::string
443 wordFromStringstream(std::istream& in, int& pos) {
444  std::stringstream ss;
445  while (isalpha(in.peek())) {
446  ss << (char) in.get();
447  }
448  pos += ss.str().size();
449  return (ss.str());
450 }
451 
452 std::string
453 numberFromStringstream(std::istream& in, int& pos) {
454  std::stringstream ss;
455  while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
456  in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
457  ss << (char) in.get();
458  }
459  pos += ss.str().size();
460  return (ss.str());
461 }
462 
463 // Should we change from IntElement and DoubleElement to NumberElement
464 // that can also hold an e value? (and have specific getters if the
465 // value is larger than an int can handle)
466 //
468 fromStringstreamNumber(std::istream& in, const std::string& file,
469  const int line, int& pos) {
470  // Remember position where the value starts. It will be set in the
471  // Position structure of the Element to be created.
472  const uint32_t start_pos = pos;
473  // This will move the pos to the end of the value.
474  const std::string number = numberFromStringstream(in, pos);
475 
476  if (number.find_first_of(".eE") < number.size()) {
477  try {
478  return (Element::create(boost::lexical_cast<double>(number),
479  Element::Position(file, line, start_pos)));
480  } catch (const boost::bad_lexical_cast&) {
481  throwJSONError(std::string("Number overflow: ") + number,
482  file, line, start_pos);
483  }
484  } else {
485  try {
486  return (Element::create(boost::lexical_cast<int64_t>(number),
487  Element::Position(file, line, start_pos)));
488  } catch (const boost::bad_lexical_cast&) {
489  throwJSONError(std::string("Number overflow: ") + number, file,
490  line, start_pos);
491  }
492  }
493  return (ElementPtr());
494 }
495 
497 fromStringstreamBool(std::istream& in, const std::string& file,
498  const int line, int& pos) {
499  // Remember position where the value starts. It will be set in the
500  // Position structure of the Element to be created.
501  const uint32_t start_pos = pos;
502  // This will move the pos to the end of the value.
503  const std::string word = wordFromStringstream(in, pos);
504 
505  if (word == "true") {
506  return (Element::create(true, Element::Position(file, line,
507  start_pos)));
508  } else if (word == "false") {
509  return (Element::create(false, Element::Position(file, line,
510  start_pos)));
511  } else {
512  throwJSONError(std::string("Bad boolean value: ") + word, file,
513  line, start_pos);
514  }
515  return (ElementPtr());
516 }
517 
519 fromStringstreamNull(std::istream& in, const std::string& file,
520  const int line, int& pos) {
521  // Remember position where the value starts. It will be set in the
522  // Position structure of the Element to be created.
523  const uint32_t start_pos = pos;
524  // This will move the pos to the end of the value.
525  const std::string word = wordFromStringstream(in, pos);
526  if (word == "null") {
527  return (Element::create(Element::Position(file, line, start_pos)));
528  } else {
529  throwJSONError(std::string("Bad null value: ") + word, file,
530  line, start_pos);
531  return (ElementPtr());
532  }
533 }
534 
536 fromStringstreamString(std::istream& in, const std::string& file, int& line,
537  int& pos) {
538  // Remember position where the value starts. It will be set in the
539  // Position structure of the Element to be created.
540  const uint32_t start_pos = pos;
541  // This will move the pos to the end of the value.
542  const std::string string_value = strFromStringstream(in, file, line, pos);
543  return (Element::create(string_value, Element::Position(file, line,
544  start_pos)));
545 }
546 
548 fromStringstreamList(std::istream& in, const std::string& file, int& line,
549  int& pos) {
550  int c = 0;
551  ElementPtr list = Element::createList(Element::Position(file, line, pos));
552  ElementPtr cur_list_element;
553 
554  skipChars(in, WHITESPACE, line, pos);
555  while (c != EOF && c != ']') {
556  if (in.peek() != ']') {
557  cur_list_element = Element::fromJSON(in, file, line, pos);
558  list->add(cur_list_element);
559  c = skipTo(in, file, line, pos, ",]", WHITESPACE);
560  } else {
561  c = in.get();
562  ++pos;
563  }
564  }
565  return (list);
566 }
567 
569 fromStringstreamMap(std::istream& in, const std::string& file, int& line,
570  int& pos) {
571  ElementPtr map = Element::createMap(Element::Position(file, line, pos));
572  skipChars(in, WHITESPACE, line, pos);
573  int c = in.peek();
574  if (c == EOF) {
575  throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
576  } else if (c == '}') {
577  // empty map, skip closing curly
578  in.ignore();
579  } else {
580  while (c != EOF && c != '}') {
581  std::string key = strFromStringstream(in, file, line, pos);
582 
583  skipTo(in, file, line, pos, ":", WHITESPACE);
584  // skip the :
585 
586  ConstElementPtr value = Element::fromJSON(in, file, line, pos);
587  map->set(key, value);
588 
589  c = skipTo(in, file, line, pos, ",}", WHITESPACE);
590  }
591  }
592  return (map);
593 }
594 } // end anonymous namespace
595 
596 std::string
597 Element::typeToName(Element::types type) {
598  switch (type) {
599  case Element::integer:
600  return (std::string("integer"));
601  case Element::real:
602  return (std::string("real"));
603  case Element::boolean:
604  return (std::string("boolean"));
605  case Element::string:
606  return (std::string("string"));
607  case Element::list:
608  return (std::string("list"));
609  case Element::map:
610  return (std::string("map"));
611  case Element::null:
612  return (std::string("null"));
613  case Element::any:
614  return (std::string("any"));
615  default:
616  return (std::string("unknown"));
617  }
618 }
619 
621 Element::nameToType(const std::string& type_name) {
622  if (type_name == "integer") {
623  return (Element::integer);
624  } else if (type_name == "real") {
625  return (Element::real);
626  } else if (type_name == "boolean") {
627  return (Element::boolean);
628  } else if (type_name == "string") {
629  return (Element::string);
630  } else if (type_name == "list") {
631  return (Element::list);
632  } else if (type_name == "map") {
633  return (Element::map);
634  } else if (type_name == "named_set") {
635  return (Element::map);
636  } else if (type_name == "null") {
637  return (Element::null);
638  } else if (type_name == "any") {
639  return (Element::any);
640  } else {
641  isc_throw(TypeError, type_name + " is not a valid type name");
642  }
643 }
644 
646 Element::fromJSON(std::istream& in, bool preproc) {
647 
648  int line = 1, pos = 1;
649  stringstream filtered;
650  if (preproc) {
651  preprocess(in, filtered);
652  }
653 
654  ElementPtr value = fromJSON(preproc ? filtered : in, "<istream>", line, pos);
655 
656  return (value);
657 }
658 
660 Element::fromJSON(std::istream& in, const std::string& file_name, bool preproc) {
661  int line = 1, pos = 1;
662  stringstream filtered;
663  if (preproc) {
664  preprocess(in, filtered);
665  }
666  return (fromJSON(preproc ? filtered : in, file_name, line, pos));
667 }
668 
670 Element::fromJSON(std::istream& in, const std::string& file, int& line,
671  int& pos) {
672  int c = 0;
673  ElementPtr element;
674  bool el_read = false;
675  skipChars(in, WHITESPACE, line, pos);
676  while (c != EOF && !el_read) {
677  c = in.get();
678  pos++;
679  switch(c) {
680  case '1':
681  case '2':
682  case '3':
683  case '4':
684  case '5':
685  case '6':
686  case '7':
687  case '8':
688  case '9':
689  case '0':
690  case '-':
691  case '+':
692  case '.':
693  in.putback(c);
694  --pos;
695  element = fromStringstreamNumber(in, file, line, pos);
696  el_read = true;
697  break;
698  case 't':
699  case 'f':
700  in.putback(c);
701  --pos;
702  element = fromStringstreamBool(in, file, line, pos);
703  el_read = true;
704  break;
705  case 'n':
706  in.putback(c);
707  --pos;
708  element = fromStringstreamNull(in, file, line, pos);
709  el_read = true;
710  break;
711  case '"':
712  in.putback('"');
713  --pos;
714  element = fromStringstreamString(in, file, line, pos);
715  el_read = true;
716  break;
717  case '[':
718  element = fromStringstreamList(in, file, line, pos);
719  el_read = true;
720  break;
721  case '{':
722  element = fromStringstreamMap(in, file, line, pos);
723  el_read = true;
724  break;
725  case EOF:
726  break;
727  default:
728  throwJSONError(std::string("error: unexpected character ") + std::string(1, c), file, line, pos);
729  break;
730  }
731  }
732  if (el_read) {
733  return (element);
734  } else {
735  isc_throw(JSONError, "nothing read");
736  }
737 }
738 
740 Element::fromJSON(const std::string& in, bool preproc) {
741  std::stringstream ss;
742  ss << in;
743 
744  int line = 1, pos = 1;
745  stringstream filtered;
746  if (preproc) {
747  preprocess(ss, filtered);
748  }
749  ElementPtr result(fromJSON(preproc ? filtered : ss, "<string>", line, pos));
750  skipChars(ss, WHITESPACE, line, pos);
751  // ss must now be at end
752  if (ss.peek() != EOF) {
753  throwJSONError("Extra data", "<string>", line, pos);
754  }
755  return result;
756 }
757 
759 Element::fromJSONFile(const std::string& file_name, bool preproc) {
760  // zero out the errno to be safe
761  errno = 0;
762 
763  std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
764  if (!infile.is_open()) {
765  const char* error = strerror(errno);
766  isc_throw(InvalidOperation, "failed to read file '" << file_name
767  << "': " << error);
768  }
769 
770  return (fromJSON(infile, file_name, preproc));
771 }
772 
773 // to JSON format
774 
775 void
776 IntElement::toJSON(std::ostream& ss) const {
777  ss << intValue();
778 }
779 
780 void
781 DoubleElement::toJSON(std::ostream& ss) const {
782  // The default output for doubles nicely drops off trailing
783  // zeros, however this produces strings without decimal points
784  // for whole number values. When reparsed this will create
785  // IntElements not DoubleElements. Rather than used a fixed
786  // precision, we'll just tack on an ".0" when the decimal point
787  // is missing.
788  ostringstream val_ss;
789  val_ss << doubleValue();
790  ss << val_ss.str();
791  if (val_ss.str().find_first_of('.') == string::npos) {
792  ss << ".0";
793  }
794 }
795 
796 void
797 BoolElement::toJSON(std::ostream& ss) const {
798  if (boolValue()) {
799  ss << "true";
800  } else {
801  ss << "false";
802  }
803 }
804 
805 void
806 NullElement::toJSON(std::ostream& ss) const {
807  ss << "null";
808 }
809 
810 void
811 StringElement::toJSON(std::ostream& ss) const {
812  ss << "\"";
813  const std::string& str = stringValue();
814  for (size_t i = 0; i < str.size(); ++i) {
815  const char c = str[i];
816  // Escape characters as defined in JSON spec
817  // Note that we do not escape forward slash; this
818  // is allowed, but not mandatory.
819  switch (c) {
820  case '"':
821  ss << '\\' << c;
822  break;
823  case '\\':
824  ss << '\\' << c;
825  break;
826  case '\b':
827  ss << '\\' << 'b';
828  break;
829  case '\f':
830  ss << '\\' << 'f';
831  break;
832  case '\n':
833  ss << '\\' << 'n';
834  break;
835  case '\r':
836  ss << '\\' << 'r';
837  break;
838  case '\t':
839  ss << '\\' << 't';
840  break;
841  default:
842  if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
843  std::ostringstream esc;
844  esc << "\\u"
845  << hex
846  << setw(4)
847  << setfill('0')
848  << (static_cast<unsigned>(c) & 0xff);
849  ss << esc.str();
850  } else {
851  ss << c;
852  }
853  }
854  }
855  ss << "\"";
856 }
857 
858 void
859 ListElement::toJSON(std::ostream& ss) const {
860  ss << "[ ";
861 
862  const std::vector<ElementPtr>& v = listValue();
863  for (auto it = v.begin(); it != v.end(); ++it) {
864  if (it != v.begin()) {
865  ss << ", ";
866  }
867  (*it)->toJSON(ss);
868  }
869  ss << " ]";
870 }
871 
872 void
873 MapElement::toJSON(std::ostream& ss) const {
874  ss << "{ ";
875 
876  const std::map<std::string, ConstElementPtr>& m = mapValue();
877  for (auto it = m.begin(); it != m.end(); ++it) {
878  if (it != m.begin()) {
879  ss << ", ";
880  }
881  ss << "\"" << (*it).first << "\": ";
882  if ((*it).second) {
883  (*it).second->toJSON(ss);
884  } else {
885  ss << "None";
886  }
887  }
888  ss << " }";
889 }
890 
891 // throws when one of the types in the path (except the one
892 // we're looking for) is not a MapElement
893 // returns 0 if it could simply not be found
894 // should that also be an exception?
896 MapElement::find(const std::string& id) const {
897  const size_t sep = id.find('/');
898  if (sep == std::string::npos) {
899  return (get(id));
900  } else {
901  ConstElementPtr ce = get(id.substr(0, sep));
902  if (ce) {
903  // ignore trailing slash
904  if (sep + 1 != id.size()) {
905  return (ce->find(id.substr(sep + 1)));
906  } else {
907  return (ce);
908  }
909  } else {
910  return (ElementPtr());
911  }
912  }
913 }
914 
916 Element::fromWire(const std::string& s) {
917  std::stringstream ss;
918  ss << s;
919  int line = 0, pos = 0;
920  return (fromJSON(ss, "<wire>", line, pos));
921 }
922 
924 Element::fromWire(std::stringstream& in, int) {
925  //
926  // Check protocol version
927  //
928  //for (int i = 0 ; i < 4 ; ++i) {
929  // const unsigned char version_byte = get_byte(in);
930  // if (PROTOCOL_VERSION[i] != version_byte) {
931  // throw DecodeError("Protocol version incorrect");
932  // }
933  //}
934  //length -= 4;
935  int line = 0, pos = 0;
936  return (fromJSON(in, "<wire>", line, pos));
937 }
938 
939 void
940 MapElement::set(const std::string& key, ConstElementPtr value) {
941  m[key] = value;
942 }
943 
944 bool
945 MapElement::find(const std::string& id, ConstElementPtr& t) const {
946  try {
947  ConstElementPtr p = find(id);
948  if (p) {
949  t = p;
950  return (true);
951  }
952  } catch (const TypeError&) {
953  // ignore
954  }
955  return (false);
956 }
957 
958 bool
959 IntElement::equals(const Element& other) const {
960  return (other.getType() == Element::integer) &&
961  (i == other.intValue());
962 }
963 
964 bool
965 DoubleElement::equals(const Element& other) const {
966  return (other.getType() == Element::real) &&
967  (fabs(d - other.doubleValue()) < 1e-14);
968 }
969 
970 bool
971 BoolElement::equals(const Element& other) const {
972  return (other.getType() == Element::boolean) &&
973  (b == other.boolValue());
974 }
975 
976 bool
977 NullElement::equals(const Element& other) const {
978  return (other.getType() == Element::null);
979 }
980 
981 bool
982 StringElement::equals(const Element& other) const {
983  return (other.getType() == Element::string) &&
984  (s == other.stringValue());
985 }
986 
987 bool
988 ListElement::equals(const Element& other) const {
989  if (other.getType() == Element::list) {
990  const size_t s = size();
991  if (s != other.size()) {
992  return (false);
993  }
994  for (size_t i = 0; i < s; ++i) {
995  if (!get(i)->equals(*other.get(i))) {
996  return (false);
997  }
998  }
999  return (true);
1000  } else {
1001  return (false);
1002  }
1003 }
1004 
1005 bool
1006 MapElement::equals(const Element& other) const {
1007  if (other.getType() == Element::map) {
1008  if (size() != other.size()) {
1009  return (false);
1010  }
1011  for (auto kv : mapValue()) {
1012  auto key = kv.first;
1013  if (other.contains(key)) {
1014  if (!get(key)->equals(*other.get(key))) {
1015  return (false);
1016  }
1017  } else {
1018  return (false);
1019  }
1020  }
1021  return (true);
1022  } else {
1023  return (false);
1024  }
1025 }
1026 
1027 bool
1029  return (!p);
1030 }
1031 
1032 void
1034  if (!b) {
1035  return;
1036  }
1037  if (a->getType() != Element::map || b->getType() != Element::map) {
1038  isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1039  }
1040 
1041  // As maps do not allow entries with multiple keys, we can either iterate
1042  // over a checking for identical entries in b or vice-versa. As elements
1043  // are removed from a if a match is found, we choose to iterate over b to
1044  // avoid problems with element removal affecting the iterator.
1045  for (auto kv : b->mapValue()) {
1046  auto key = kv.first;
1047  if (a->contains(key)) {
1048  if (a->get(key)->equals(*b->get(key))) {
1049  a->remove(key);
1050  }
1051  }
1052  }
1053 }
1054 
1057  ElementPtr result = Element::createMap();
1058 
1059  if (!b) {
1060  return (result);
1061  }
1062 
1063  if (a->getType() != Element::map || b->getType() != Element::map) {
1064  isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1065  }
1066 
1067  for (auto kv : a->mapValue()) {
1068  auto key = kv.first;
1069  if (!b->contains(key) ||
1070  !a->get(key)->equals(*b->get(key))) {
1071  result->set(key, kv.second);
1072  }
1073  }
1074 
1075  return (result);
1076 }
1077 
1078 void
1080  if (element->getType() != Element::map ||
1081  other->getType() != Element::map) {
1082  isc_throw(TypeError, "merge arguments not MapElements");
1083  }
1084 
1085  for (auto kv : other->mapValue()) {
1086  auto key = kv.first;
1087  auto value = kv.second;
1088  if (value && value->getType() != Element::null) {
1089  element->set(key, value);
1090  } else if (element->contains(key)) {
1091  element->remove(key);
1092  }
1093  }
1094 }
1095 
1096 ElementPtr
1097 copy(ConstElementPtr from, int level) {
1098  if (!from) {
1099  isc_throw(BadValue, "copy got a null pointer");
1100  }
1101  int from_type = from->getType();
1102  if (from_type == Element::integer) {
1103  return (ElementPtr(new IntElement(from->intValue())));
1104  } else if (from_type == Element::real) {
1105  return (ElementPtr(new DoubleElement(from->doubleValue())));
1106  } else if (from_type == Element::boolean) {
1107  return (ElementPtr(new BoolElement(from->boolValue())));
1108  } else if (from_type == Element::null) {
1109  return (ElementPtr(new NullElement()));
1110  } else if (from_type == Element::string) {
1111  return (ElementPtr(new StringElement(from->stringValue())));
1112  } else if (from_type == Element::list) {
1113  ElementPtr result = ElementPtr(new ListElement());
1114  for (auto elem : from->listValue()) {
1115  if (level == 0) {
1116  result->add(elem);
1117  } else {
1118  result->add(copy(elem, level - 1));
1119  }
1120  }
1121  return (result);
1122  } else if (from_type == Element::map) {
1123  ElementPtr result = ElementPtr(new MapElement());
1124  for (auto kv : from->mapValue()) {
1125  auto key = kv.first;
1126  auto value = kv.second;
1127  if (level == 0) {
1128  result->set(key, value);
1129  } else {
1130  result->set(key, copy(value, level - 1));
1131  }
1132  }
1133  return (result);
1134  } else {
1135  isc_throw(BadValue, "copy got an element of type: " << from_type);
1136  }
1137 }
1138 
1139 namespace {
1140 
1141 // Helper function which blocks infinite recursion
1142 bool
1143 isEquivalent0(ConstElementPtr a, ConstElementPtr b, unsigned level) {
1144  // check looping forever on cycles
1145  if (!level) {
1146  isc_throw(BadValue, "isEquivalent got infinite recursion: "
1147  "arguments include cycles");
1148  }
1149  if (!a || !b) {
1150  isc_throw(BadValue, "isEquivalent got a null pointer");
1151  }
1152  // check types
1153  if (a->getType() != b->getType()) {
1154  return (false);
1155  }
1156  if (a->getType() == Element::list) {
1157  // check empty
1158  if (a->empty()) {
1159  return (b->empty());
1160  }
1161  // check size
1162  if (a->size() != b->size()) {
1163  return (false);
1164  }
1165 
1166  // copy b into a list
1167  const size_t s = a->size();
1168  std::list<ConstElementPtr> l;
1169  for (size_t i = 0; i < s; ++i) {
1170  l.push_back(b->get(i));
1171  }
1172 
1173  // iterate on a
1174  for (size_t i = 0; i < s; ++i) {
1175  ConstElementPtr item = a->get(i);
1176  // lookup this item in the list
1177  bool found = false;
1178  for (auto it = l.begin(); it != l.end(); ++it) {
1179  // if found in the list remove it
1180  if (isEquivalent0(item, *it, level - 1)) {
1181  found = true;
1182  l.erase(it);
1183  break;
1184  }
1185  }
1186  // if not found argument differs
1187  if (!found) {
1188  return (false);
1189  }
1190  }
1191 
1192  // sanity check: the list must be empty
1193  if (!l.empty()) {
1194  isc_throw(Unexpected, "isEquivalent internal error");
1195  }
1196  return (true);
1197  } else if (a->getType() == Element::map) {
1198  // check sizes
1199  if (a->size() != b->size()) {
1200  return (false);
1201  }
1202  // iterate on the first map
1203  for (auto kv : a->mapValue()) {
1204  // get the b value for the given keyword and recurse
1205  ConstElementPtr item = b->get(kv.first);
1206  if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1207  return (false);
1208  }
1209  }
1210  return (true);
1211  } else {
1212  return (a->equals(*b));
1213  }
1214 }
1215 
1216 } // end anonymous namespace
1217 
1218 bool
1220  return (isEquivalent0(a, b, 100));
1221 }
1222 
1223 void
1224 prettyPrint(ConstElementPtr element, std::ostream& out,
1225  unsigned indent, unsigned step) {
1226  if (!element) {
1227  isc_throw(BadValue, "prettyPrint got a null pointer");
1228  }
1229  if (element->getType() == Element::list) {
1230  // empty list case
1231  if (element->empty()) {
1232  out << "[ ]";
1233  return;
1234  }
1235 
1236  // complex ? multiline : oneline
1237  if (!element->get(0)) {
1238  isc_throw(BadValue, "prettyPrint got a null pointer");
1239  }
1240  int first_type = element->get(0)->getType();
1241  bool complex = false;
1242  if ((first_type == Element::list) || (first_type == Element::map)) {
1243  complex = true;
1244  }
1245  std::string separator = complex ? ",\n" : ", ";
1246 
1247  // open the list
1248  out << "[" << (complex ? "\n" : " ");
1249 
1250  // iterate on items
1251  const auto& l = element->listValue();
1252  for (auto it = l.begin(); it != l.end(); ++it) {
1253  // add the separator if not the first item
1254  if (it != l.begin()) {
1255  out << separator;
1256  }
1257  // add indentation
1258  if (complex) {
1259  out << std::string(indent + step, ' ');
1260  }
1261  // recursive call
1262  prettyPrint(*it, out, indent + step, step);
1263  }
1264 
1265  // close the list
1266  if (complex) {
1267  out << "\n" << std::string(indent, ' ');
1268  } else {
1269  out << " ";
1270  }
1271  out << "]";
1272  } else if (element->getType() == Element::map) {
1273  // empty map case
1274  if (element->size() == 0) {
1275  out << "{ }";
1276  return;
1277  }
1278 
1279  // open the map
1280  out << "{\n";
1281 
1282  // iterate on keyword: value
1283  const auto& m = element->mapValue();
1284  bool first = true;
1285  for (auto it = m.begin(); it != m.end(); ++it) {
1286  // add the separator if not the first item
1287  if (first) {
1288  first = false;
1289  } else {
1290  out << ",\n";
1291  }
1292  // add indentation
1293  out << std::string(indent + step, ' ');
1294  // add keyword:
1295  out << "\"" << it->first << "\": ";
1296  // recursive call
1297  prettyPrint(it->second, out, indent + step, step);
1298  }
1299 
1300  // close the map
1301  out << "\n" << std::string(indent, ' ') << "}";
1302  } else {
1303  // not a list or a map
1304  element->toJSON(out);
1305  }
1306 }
1307 
1308 std::string
1309 prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
1310  std::stringstream ss;
1311  prettyPrint(element, ss, indent, step);
1312  return (ss.str());
1313 }
1314 
1315 void Element::preprocess(std::istream& in, std::stringstream& out) {
1316 
1317  std::string line;
1318 
1319  while (std::getline(in, line)) {
1320  // If this is a comments line, replace it with empty line
1321  // (so the line numbers will still match
1322  if (!line.empty() && line[0] == '#') {
1323  line = "";
1324  }
1325 
1326  // getline() removes end line characters. Unfortunately, we need
1327  // it for getting the line numbers right (in case we report an
1328  // error.
1329  out << line;
1330  out << "\n";
1331  }
1332 }
1333 
1334 } // end of isc::data namespace
1335 } // end of isc namespace
virtual double doubleValue() const
Definition: data.h:213
std::string prettyPrint(ConstElementPtr element, unsigned indent, unsigned step)
Pretty prints the data into string.
Definition: data.cc:1309
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
std::string str() const
Returns the position in the textual format.
Definition: data.cc:38
bool operator!=(const Element &a, const Element &b)
Definition: data.cc:214
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
Definition: data.cc:1219
int getType() const
Definition: data.h:156
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
STL namespace.
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.
Definition: data.cc:180
#define throwTypeError(error)
Add the position to a TypeError message should be used in place of isc_throw(TypeError, error)
Definition: data.h:183
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
Definition: data.cc:1028
virtual size_t size() const
Returns the number of elements in the list.
Definition: data.cc:155
virtual bool equals(const Element &other) const =0
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...
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1097
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:1056
virtual bool boolValue() const
Definition: data.h:215
Notes: IntElement type is changed to int64_t.
Definition: data.h:544
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
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
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition: data.h:30
Represents the position of the data element within a configuration string.
Definition: data.h:88
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::ostream & operator<<(std::ostream &out, const Element &e)
Insert the Element as a string into stream.
Definition: data.cc:205
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
Definition: data.cc:1079
virtual ConstElementPtr get(const int i) const
Returns the ElementPtr at the given index.
Definition: data.cc:130
A generic exception that is thrown if a function is called in a prohibited way.
The Element class represents a piece of data, used by the command channel and configuration parts...
Definition: data.h:66
virtual int64_t intValue() const
Definition: data.h:211
uint16_t pos_
The position (offset from the beginning) in the buffer where the name starts.
virtual std::string stringValue() const
Definition: data.h:217