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