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