24 #include <boost/lexical_cast.hpp>
31 const char*
const WHITESPACE =
" \b\f\n\r\t";
38 Element::Position::str()
const {
39 std::ostringstream ss;
40 ss << file_ <<
":" << line_ <<
":" <<
pos_;
51 Element::str()
const {
58 Element::toWire()
const {
65 Element::toWire(std::ostream& ss)
const {
70 Element::getValue(int64_t&)
const {
75 Element::getValue(
double&)
const {
80 Element::getValue(
bool&)
const {
85 Element::getValue(std::string&)
const {
90 Element::getValue(std::vector<ElementPtr>&)
const {
95 Element::getValue(std::map<std::string, ConstElementPtr>&)
const {
100 Element::setValue(
const long long int) {
105 Element::setValue(
const double) {
110 Element::setValue(
const bool) {
115 Element::setValue(
const std::string&) {
120 Element::setValue(
const std::vector<ElementPtr>&) {
125 Element::setValue(
const std::map<std::string, ConstElementPtr>&) {
130 Element::get(
const int)
const {
135 Element::getNonConst(
const int)
const {
150 Element::remove(
const int) {
155 Element::size()
const {
160 Element::empty()
const {
165 Element::get(
const std::string&)
const {
175 Element::remove(
const std::string&) {
180 Element::contains(
const std::string&)
const {
185 Element::find(
const std::string&)
const {
196 throwJSONError(
const std::string&
error,
const std::string& file,
int line,
198 std::stringstream ss;
199 ss << error <<
" in " + file +
":" << line <<
":" << pos;
206 return (out << e.
str());
227 Element::create(
const long long int i,
const Position& pos) {
232 Element::create(
const int i,
const Position& pos) {
233 return (create(static_cast<long long int>(i), pos));
237 Element::create(
const long int i,
const Position& pos) {
238 return (create(static_cast<long long int>(i), pos));
242 Element::create(
const double d,
const Position& pos) {
247 Element::create(
const bool b,
const Position& pos) {
252 Element::create(
const std::string& s,
const Position& pos) {
257 Element::create(
const char *s,
const Position& pos) {
258 return (create(std::string(s), pos));
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) {
288 skipChars(std::istream& in,
const char* chars,
int& line,
int& pos) {
290 while (charIn(c, chars) && c != EOF) {
308 skipTo(std::istream& in,
const std::string& file,
int& line,
int& pos,
309 const char* chars,
const char* may_skip=
"") {
317 if (charIn(c, may_skip)) {
320 }
else if (charIn(c, chars)) {
321 while (charIn(in.peek(), may_skip)) {
322 if (in.peek() ==
'\n') {
332 throwJSONError(std::string(
"'") + std::string(1, c) +
"' read, one of \"" + chars +
"\" expected", file, line, pos);
335 throwJSONError(std::string(
"EOF read, one of \"") + chars +
"\" expected", file, line, pos);
342 strFromStringstream(std::istream& in,
const std::string& file,
343 const int line,
int& pos) {
344 std::stringstream ss;
351 throwJSONError(
"String expected", file, line, pos);
354 while (c != EOF && c !=
'"') {
389 throwJSONError(
"Unsupported unicode escape", file, line, pos);
396 throwJSONError(
"Unsupported unicode escape", file, line, pos - 2);
402 if ((d >=
'0') && (d <=
'9')) {
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;
409 throwJSONError(
"Not hexadecimal in unicode escape", file, line, pos - 3);
415 if ((d >=
'0') && (d <=
'9')) {
417 }
else if ((d >=
'A') && (d <=
'F')) {
419 }
else if ((d >=
'a') && (d <=
'f')) {
422 throwJSONError(
"Not hexadecimal in unicode escape", file, line, pos - 4);
426 throwJSONError(
"Bad escape", file, line, pos);
437 throwJSONError(
"Unterminated string", file, line, pos);
443 wordFromStringstream(std::istream& in,
int& pos) {
444 std::stringstream ss;
445 while (isalpha(in.peek())) {
446 ss << (char) in.get();
448 pos += ss.str().size();
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();
459 pos += ss.str().size();
468 fromStringstreamNumber(std::istream& in,
const std::string& file,
469 const int line,
int& pos) {
472 const uint32_t start_pos = pos;
474 const std::string number = numberFromStringstream(in, pos);
476 if (number.find_first_of(
".eE") < number.size()) {
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);
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,
497 fromStringstreamBool(std::istream& in,
const std::string& file,
498 const int line,
int& pos) {
501 const uint32_t start_pos = pos;
503 const std::string word = wordFromStringstream(in, pos);
505 if (word ==
"true") {
506 return (Element::create(
true, Element::Position(file, line,
508 }
else if (word ==
"false") {
509 return (Element::create(
false, Element::Position(file, line,
512 throwJSONError(std::string(
"Bad boolean value: ") + word, file,
519 fromStringstreamNull(std::istream& in,
const std::string& file,
520 const int line,
int& pos) {
523 const uint32_t start_pos = pos;
525 const std::string word = wordFromStringstream(in, pos);
526 if (word ==
"null") {
527 return (Element::create(Element::Position(file, line, start_pos)));
529 throwJSONError(std::string(
"Bad null value: ") + word, file,
536 fromStringstreamString(std::istream& in,
const std::string& file,
int& line,
540 const uint32_t start_pos = pos;
542 const std::string string_value = strFromStringstream(in, file, line, pos);
543 return (Element::create(string_value, Element::Position(file, line,
548 fromStringstreamList(std::istream& in,
const std::string& file,
int& line,
551 ElementPtr list = Element::createList(Element::Position(file, line, pos));
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);
569 fromStringstreamMap(std::istream& in,
const std::string& file,
int& line,
571 ElementPtr map = Element::createMap(Element::Position(file, line, pos));
572 skipChars(in, WHITESPACE, line, pos);
575 throwJSONError(std::string(
"Unterminated map, <string> or } expected"), file, line, pos);
576 }
else if (c ==
'}') {
580 while (c != EOF && c !=
'}') {
581 std::string key = strFromStringstream(in, file, line, pos);
583 skipTo(in, file, line, pos,
":", WHITESPACE);
587 map->set(key, value);
589 c = skipTo(in, file, line, pos,
",}", WHITESPACE);
599 case Element::integer:
600 return (std::string(
"integer"));
602 return (std::string(
"real"));
603 case Element::boolean:
604 return (std::string(
"boolean"));
605 case Element::string:
606 return (std::string(
"string"));
608 return (std::string(
"list"));
610 return (std::string(
"map"));
612 return (std::string(
"null"));
614 return (std::string(
"any"));
616 return (std::string(
"unknown"));
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);
646 Element::fromJSON(std::istream& in,
bool preproc) {
648 int line = 1, pos = 1;
649 stringstream filtered;
651 preprocess(in, filtered);
654 ElementPtr value = fromJSON(preproc ? filtered : in,
"<istream>", line, pos);
660 Element::fromJSON(std::istream& in,
const std::string& file_name,
bool preproc) {
661 int line = 1, pos = 1;
662 stringstream filtered;
664 preprocess(in, filtered);
666 return (fromJSON(preproc ? filtered : in, file_name, line, pos));
670 Element::fromJSON(std::istream& in,
const std::string& file,
int& line,
674 bool el_read =
false;
675 skipChars(in, WHITESPACE, line, pos);
676 while (c != EOF && !el_read) {
695 element = fromStringstreamNumber(in, file, line, pos);
702 element = fromStringstreamBool(in, file, line, pos);
708 element = fromStringstreamNull(in, file, line, pos);
714 element = fromStringstreamString(in, file, line, pos);
718 element = fromStringstreamList(in, file, line, pos);
722 element = fromStringstreamMap(in, file, line, pos);
728 throwJSONError(std::string(
"error: unexpected character ") + std::string(1, c), file, line, pos);
740 Element::fromJSON(
const std::string& in,
bool preproc) {
741 std::stringstream ss;
744 int line = 1, pos = 1;
745 stringstream filtered;
747 preprocess(ss, filtered);
749 ElementPtr result(fromJSON(preproc ? filtered : ss,
"<string>", line, pos));
750 skipChars(ss, WHITESPACE, line, pos);
752 if (ss.peek() != EOF) {
753 throwJSONError(
"Extra data",
"<string>", line, pos);
759 Element::fromJSONFile(
const std::string& file_name,
bool preproc) {
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);
770 return (fromJSON(infile, file_name, preproc));
776 IntElement::toJSON(std::ostream& ss)
const {
781 DoubleElement::toJSON(std::ostream& ss)
const {
788 ostringstream val_ss;
789 val_ss << doubleValue();
791 if (val_ss.str().find_first_of(
'.') == string::npos) {
797 BoolElement::toJSON(std::ostream& ss)
const {
806 NullElement::toJSON(std::ostream& ss)
const {
811 StringElement::toJSON(std::ostream& ss)
const {
813 const std::string& str = stringValue();
814 for (
size_t i = 0; i < str.size(); ++i) {
815 const char c = str[i];
842 if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
843 std::ostringstream esc;
848 << (
static_cast<unsigned>(c) & 0xff);
859 ListElement::toJSON(std::ostream& ss)
const {
862 const std::vector<ElementPtr>& v = listValue();
863 for (
auto it = v.begin(); it != v.end(); ++it) {
864 if (it != v.begin()) {
873 MapElement::toJSON(std::ostream& ss)
const {
876 const std::map<std::string, ConstElementPtr>& m = mapValue();
877 for (
auto it = m.begin(); it != m.end(); ++it) {
878 if (it != m.begin()) {
881 ss <<
"\"" << (*it).first <<
"\": ";
883 (*it).second->toJSON(ss);
896 MapElement::find(
const std::string&
id)
const {
897 const size_t sep =
id.find(
'/');
898 if (sep == std::string::npos) {
904 if (sep + 1 !=
id.size()) {
905 return (ce->find(
id.substr(sep + 1)));
916 Element::fromWire(
const std::string& s) {
917 std::stringstream ss;
919 int line = 0, pos = 0;
920 return (fromJSON(ss,
"<wire>", line, pos));
924 Element::fromWire(std::stringstream& in,
int) {
935 int line = 0, pos = 0;
936 return (fromJSON(in,
"<wire>", line, pos));
959 IntElement::equals(
const Element& other)
const {
960 return (other.
getType() == Element::integer) &&
965 DoubleElement::equals(
const Element& other)
const {
966 return (other.
getType() == Element::real) &&
971 BoolElement::equals(
const Element& other)
const {
972 return (other.
getType() == Element::boolean) &&
977 NullElement::equals(
const Element& other)
const {
978 return (other.
getType() == Element::null);
982 StringElement::equals(
const Element& other)
const {
983 return (other.
getType() == Element::string) &&
988 ListElement::equals(
const Element& other)
const {
989 if (other.
getType() == Element::list) {
990 const size_t s = size();
991 if (s != other.
size()) {
994 for (
size_t i = 0; i < s; ++i) {
995 if (!
get(i)->equals(*other.
get(i))) {
1007 if (other.
getType() == Element::map) {
1008 if (size() != other.
size()) {
1011 for (
auto kv : mapValue()) {
1012 auto key = kv.first;
1014 if (!
get(key)->equals(*other.
get(key))) {
1037 if (a->getType() != Element::map || b->getType() != Element::map) {
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))) {
1063 if (a->getType() != Element::map || b->getType() != Element::map) {
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);
1080 if (element->getType() != Element::map ||
1081 other->getType() != Element::map) {
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);
1101 int from_type = from->getType();
1102 if (from_type == Element::integer) {
1104 }
else if (from_type == Element::real) {
1106 }
else if (from_type == Element::boolean) {
1108 }
else if (from_type == Element::null) {
1110 }
else if (from_type == Element::string) {
1112 }
else if (from_type == Element::list) {
1114 for (
auto elem : from->listValue()) {
1118 result->add(
copy(elem, level - 1));
1122 }
else if (from_type == Element::map) {
1124 for (
auto kv : from->mapValue()) {
1125 auto key = kv.first;
1126 auto value = kv.second;
1128 result->set(key, value);
1130 result->set(key,
copy(value, level - 1));
1147 "arguments include cycles");
1150 isc_throw(BadValue,
"isEquivalent got a null pointer");
1153 if (a->getType() != b->getType()) {
1156 if (a->getType() == Element::list) {
1159 return (b->empty());
1162 if (a->size() != b->size()) {
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));
1174 for (
size_t i = 0; i < s; ++i) {
1178 for (
auto it = l.begin(); it != l.end(); ++it) {
1180 if (isEquivalent0(item, *it, level - 1)) {
1194 isc_throw(Unexpected,
"isEquivalent internal error");
1197 }
else if (a->getType() == Element::map) {
1199 if (a->size() != b->size()) {
1203 for (
auto kv : a->mapValue()) {
1206 if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1212 return (a->equals(*b));
1220 return (isEquivalent0(a, b, 100));
1225 unsigned indent,
unsigned step) {
1229 if (element->getType() == Element::list) {
1231 if (element->empty()) {
1237 if (!element->get(0)) {
1240 int first_type = element->get(0)->getType();
1241 bool complex =
false;
1242 if ((first_type == Element::list) || (first_type == Element::map)) {
1245 std::string separator = complex ?
",\n" :
", ";
1248 out <<
"[" << (complex ?
"\n" :
" ");
1251 const auto& l = element->listValue();
1252 for (
auto it = l.begin(); it != l.end(); ++it) {
1254 if (it != l.begin()) {
1259 out << std::string(indent + step,
' ');
1267 out <<
"\n" << std::string(indent,
' ');
1272 }
else if (element->getType() == Element::map) {
1274 if (element->size() == 0) {
1283 const auto& m = element->mapValue();
1285 for (
auto it = m.begin(); it != m.end(); ++it) {
1293 out << std::string(indent + step,
' ');
1295 out <<
"\"" << it->first <<
"\": ";
1297 prettyPrint(it->second, out, indent + step, step);
1301 out <<
"\n" << std::string(indent,
' ') <<
"}";
1304 element->toJSON(out);
1310 std::stringstream ss;
1315 void Element::preprocess(std::istream& in, std::stringstream& out) {
1319 while (std::getline(in, line)) {
1322 if (!line.empty() && line[0] ==
'#') {
virtual double doubleValue() const
std::string prettyPrint(ConstElementPtr element, unsigned indent, unsigned step)
Pretty prints the data into string.
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
std::string str() const
Returns the position in the textual format.
bool operator!=(const Element &a, const Element &b)
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
boost::shared_ptr< Element > ElementPtr
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.
#define throwTypeError(error)
Add the position to a TypeError message should be used in place of isc_throw(TypeError, error)
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
virtual size_t size() const
Returns the number of elements in the list.
virtual bool equals(const Element &other) const =0
bool operator==(const Element &a, const Element &b)
#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.
ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b)
Create a new ElementPtr from the first ElementPtr, removing all values that are equal in the second...
virtual bool boolValue() const
Notes: IntElement type is changed to int64_t.
boost::shared_ptr< const Element > ConstElementPtr
std::string str() const
Returns a string representing the Element and all its child elements; note that this is different fro...
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Represents the position of the data element within a configuration string.
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.
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
virtual ConstElementPtr get(const int i) const
Returns the ElementPtr at the given index.
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...
virtual int64_t intValue() const
uint16_t pos_
The position (offset from the beginning) in the buffer where the name starts.
virtual std::string stringValue() const