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());
224 case Element::integer:
228 case Element::boolean:
230 case Element::string:
246 Element::create(
const long long int i,
const Position& pos) {
251 Element::create(
const int i,
const Position& pos) {
252 return (create(static_cast<long long int>(i), pos));
256 Element::create(
const long int i,
const Position& pos) {
257 return (create(static_cast<long long int>(i), pos));
261 Element::create(
const uint32_t i,
const Position& pos) {
262 return (create(static_cast<long long int>(i), pos));
266 Element::create(
const double d,
const Position& pos) {
271 Element::create(
const bool b,
const Position& pos) {
276 Element::create(
const std::string& s,
const Position& pos) {
281 Element::create(
const char *s,
const Position& pos) {
282 return (create(std::string(s), pos));
301 charIn(
const int c,
const char* chars) {
302 const size_t chars_len = std::strlen(chars);
303 for (
size_t i = 0; i < chars_len; ++i) {
312 skipChars(std::istream& in,
const char* chars,
int& line,
int& pos) {
314 while (charIn(c, chars) && c != EOF) {
332 skipTo(std::istream& in,
const std::string& file,
int& line,
int& pos,
333 const char* chars,
const char* may_skip=
"") {
341 if (charIn(c, may_skip)) {
344 }
else if (charIn(c, chars)) {
345 while (charIn(in.peek(), may_skip)) {
346 if (in.peek() ==
'\n') {
356 throwJSONError(std::string(
"'") + std::string(1, c) +
"' read, one of \"" + chars +
"\" expected", file, line, pos);
359 throwJSONError(std::string(
"EOF read, one of \"") + chars +
"\" expected", file, line, pos);
366 strFromStringstream(std::istream& in,
const std::string& file,
367 const int line,
int& pos) {
368 std::stringstream ss;
375 throwJSONError(
"String expected", file, line, pos);
378 while (c != EOF && c !=
'"') {
413 throwJSONError(
"Unsupported unicode escape", file, line, pos);
420 throwJSONError(
"Unsupported unicode escape", file, line, pos - 2);
426 if ((d >=
'0') && (d <=
'9')) {
428 }
else if ((d >=
'A') && (d <=
'F')) {
429 c = (d -
'A' + 10) << 4;
430 }
else if ((d >=
'a') && (d <=
'f')) {
431 c = (d -
'a' + 10) << 4;
433 throwJSONError(
"Not hexadecimal in unicode escape", file, line, pos - 3);
439 if ((d >=
'0') && (d <=
'9')) {
441 }
else if ((d >=
'A') && (d <=
'F')) {
443 }
else if ((d >=
'a') && (d <=
'f')) {
446 throwJSONError(
"Not hexadecimal in unicode escape", file, line, pos - 4);
450 throwJSONError(
"Bad escape", file, line, pos);
461 throwJSONError(
"Unterminated string", file, line, pos);
467 wordFromStringstream(std::istream& in,
int& pos) {
468 std::stringstream ss;
469 while (isalpha(in.peek())) {
470 ss << (char) in.get();
472 pos += ss.str().size();
477 numberFromStringstream(std::istream& in,
int& pos) {
478 std::stringstream ss;
479 while (isdigit(in.peek()) || in.peek() ==
'+' || in.peek() ==
'-' ||
480 in.peek() ==
'.' || in.peek() ==
'e' || in.peek() ==
'E') {
481 ss << (char) in.get();
483 pos += ss.str().size();
492 fromStringstreamNumber(std::istream& in,
const std::string& file,
493 const int line,
int& pos) {
496 const uint32_t start_pos = pos;
498 const std::string number = numberFromStringstream(in, pos);
500 if (number.find_first_of(
".eE") < number.size()) {
502 return (Element::create(boost::lexical_cast<double>(number),
504 }
catch (
const boost::bad_lexical_cast&) {
505 throwJSONError(std::string(
"Number overflow: ") + number,
506 file, line, start_pos);
510 return (Element::create(boost::lexical_cast<int64_t>(number),
512 }
catch (
const boost::bad_lexical_cast&) {
513 throwJSONError(std::string(
"Number overflow: ") + number, file,
521 fromStringstreamBool(std::istream& in,
const std::string& file,
522 const int line,
int& pos) {
525 const uint32_t start_pos = pos;
527 const std::string word = wordFromStringstream(in, pos);
529 if (word ==
"true") {
532 }
else if (word ==
"false") {
536 throwJSONError(std::string(
"Bad boolean value: ") + word, file,
543 fromStringstreamNull(std::istream& in,
const std::string& file,
544 const int line,
int& pos) {
547 const uint32_t start_pos = pos;
549 const std::string word = wordFromStringstream(in, pos);
550 if (word ==
"null") {
553 throwJSONError(std::string(
"Bad null value: ") + word, file,
560 fromStringstreamString(std::istream& in,
const std::string& file,
int& line,
564 const uint32_t start_pos = pos;
566 const std::string string_value = strFromStringstream(in, file, line, pos);
572 fromStringstreamList(std::istream& in,
const std::string& file,
int& line,
578 skipChars(in, WHITESPACE, line, pos);
579 while (c != EOF && c !=
']') {
580 if (in.peek() !=
']') {
581 cur_list_element = Element::fromJSON(in, file, line, pos);
582 list->add(cur_list_element);
583 c = skipTo(in, file, line, pos,
",]", WHITESPACE);
593 fromStringstreamMap(std::istream& in,
const std::string& file,
int& line,
596 skipChars(in, WHITESPACE, line, pos);
599 throwJSONError(std::string(
"Unterminated map, <string> or } expected"), file, line, pos);
600 }
else if (c ==
'}') {
604 while (c != EOF && c !=
'}') {
605 std::string key = strFromStringstream(in, file, line, pos);
607 skipTo(in, file, line, pos,
":", WHITESPACE);
611 map->set(key, value);
613 c = skipTo(in, file, line, pos,
",}", WHITESPACE);
623 case Element::integer:
624 return (std::string(
"integer"));
626 return (std::string(
"real"));
627 case Element::boolean:
628 return (std::string(
"boolean"));
629 case Element::string:
630 return (std::string(
"string"));
632 return (std::string(
"list"));
634 return (std::string(
"map"));
636 return (std::string(
"null"));
638 return (std::string(
"any"));
640 return (std::string(
"unknown"));
645 Element::nameToType(
const std::string& type_name) {
646 if (type_name ==
"integer") {
647 return (Element::integer);
648 }
else if (type_name ==
"real") {
649 return (Element::real);
650 }
else if (type_name ==
"boolean") {
651 return (Element::boolean);
652 }
else if (type_name ==
"string") {
653 return (Element::string);
654 }
else if (type_name ==
"list") {
655 return (Element::list);
656 }
else if (type_name ==
"map") {
657 return (Element::map);
658 }
else if (type_name ==
"named_set") {
659 return (Element::map);
660 }
else if (type_name ==
"null") {
661 return (Element::null);
662 }
else if (type_name ==
"any") {
663 return (Element::any);
670 Element::fromJSON(std::istream& in,
bool preproc) {
672 int line = 1, pos = 1;
673 stringstream filtered;
675 preprocess(in, filtered);
678 ElementPtr value = fromJSON(preproc ? filtered : in,
"<istream>", line, pos);
684 Element::fromJSON(std::istream& in,
const std::string& file_name,
bool preproc) {
685 int line = 1, pos = 1;
686 stringstream filtered;
688 preprocess(in, filtered);
690 return (fromJSON(preproc ? filtered : in, file_name, line, pos));
694 Element::fromJSON(std::istream& in,
const std::string& file,
int& line,
698 bool el_read =
false;
699 skipChars(in, WHITESPACE, line, pos);
700 while (c != EOF && !el_read) {
719 element = fromStringstreamNumber(in, file, line, pos);
726 element = fromStringstreamBool(in, file, line, pos);
732 element = fromStringstreamNull(in, file, line, pos);
738 element = fromStringstreamString(in, file, line, pos);
742 element = fromStringstreamList(in, file, line, pos);
746 element = fromStringstreamMap(in, file, line, pos);
752 throwJSONError(std::string(
"error: unexpected character ") + std::string(1, c), file, line, pos);
764 Element::fromJSON(
const std::string& in,
bool preproc) {
765 std::stringstream ss;
768 int line = 1, pos = 1;
769 stringstream filtered;
771 preprocess(ss, filtered);
773 ElementPtr result(fromJSON(preproc ? filtered : ss,
"<string>", line, pos));
774 skipChars(ss, WHITESPACE, line, pos);
776 if (ss.peek() != EOF) {
777 throwJSONError(
"Extra data",
"<string>", line, pos);
783 Element::fromJSONFile(
const std::string& file_name,
bool preproc) {
787 std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
788 if (!infile.is_open()) {
789 const char*
error = strerror(errno);
794 return (fromJSON(infile, file_name, preproc));
800 IntElement::toJSON(std::ostream& ss)
const {
805 DoubleElement::toJSON(std::ostream& ss)
const {
812 ostringstream val_ss;
813 val_ss << doubleValue();
815 if (val_ss.str().find_first_of(
'.') == string::npos) {
821 BoolElement::toJSON(std::ostream& ss)
const {
830 NullElement::toJSON(std::ostream& ss)
const {
835 StringElement::toJSON(std::ostream& ss)
const {
837 const std::string& str = stringValue();
838 for (
size_t i = 0; i < str.size(); ++i) {
839 const char c = str[i];
866 if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
867 std::ostringstream esc;
872 << (
static_cast<unsigned>(c) & 0xff);
883 ListElement::toJSON(std::ostream& ss)
const {
886 const std::vector<ElementPtr>& v = listValue();
887 for (
auto it = v.begin(); it != v.end(); ++it) {
888 if (it != v.begin()) {
897 MapElement::toJSON(std::ostream& ss)
const {
900 const std::map<std::string, ConstElementPtr>& m = mapValue();
901 for (
auto it = m.begin(); it != m.end(); ++it) {
902 if (it != m.begin()) {
905 ss <<
"\"" << (*it).first <<
"\": ";
907 (*it).second->toJSON(ss);
920 MapElement::find(
const std::string&
id)
const {
921 const size_t sep =
id.find(
'/');
922 if (sep == std::string::npos) {
928 if (sep + 1 !=
id.size()) {
929 return (ce->find(
id.substr(sep + 1)));
940 Element::fromWire(
const std::string& s) {
941 std::stringstream ss;
943 int line = 0, pos = 0;
944 return (fromJSON(ss,
"<wire>", line, pos));
948 Element::fromWire(std::stringstream& in,
int) {
959 int line = 0, pos = 0;
960 return (fromJSON(in,
"<wire>", line, pos));
983 IntElement::equals(
const Element& other)
const {
984 return (other.
getType() == Element::integer) &&
989 DoubleElement::equals(
const Element& other)
const {
990 return (other.
getType() == Element::real) &&
995 BoolElement::equals(
const Element& other)
const {
996 return (other.
getType() == Element::boolean) &&
1002 return (other.
getType() == Element::null);
1006 StringElement::equals(
const Element& other)
const {
1007 return (other.
getType() == Element::string) &&
1013 if (other.
getType() == Element::list) {
1014 const size_t s = size();
1015 if (s != other.
size()) {
1018 for (
size_t i = 0; i < s; ++i) {
1019 if (!
get(i)->equals(*other.
get(i))) {
1030 ListElement::sort(std::string
const& index ) {
1035 int const t(l.at(0)->getType());
1036 std::function<bool(ElementPtr, ElementPtr)> comparator;
1038 if (index.empty()) {
1049 }
else if (t == list) {
1054 if (!index.empty()) {
1062 std::sort(l.begin(), l.end(), comparator);
1067 if (other.
getType() == Element::map) {
1068 if (size() != other.
size()) {
1071 for (
auto kv : mapValue()) {
1072 auto key = kv.first;
1074 if (!
get(key)->equals(*other.
get(key))) {
1097 if (a->getType() != Element::map || b->getType() != Element::map) {
1105 for (
auto kv : b->mapValue()) {
1106 auto key = kv.first;
1107 if (a->contains(key)) {
1108 if (a->get(key)->equals(*b->get(key))) {
1123 if (a->getType() != Element::map || b->getType() != Element::map) {
1127 for (
auto kv : a->mapValue()) {
1128 auto key = kv.first;
1129 if (!b->contains(key) ||
1130 !a->get(key)->equals(*b->get(key))) {
1131 result->set(key, kv.second);
1140 if (element->getType() != Element::map ||
1141 other->getType() != Element::map) {
1145 for (
auto kv : other->mapValue()) {
1146 auto key = kv.first;
1147 auto value = kv.second;
1148 if (value && value->getType() != Element::null) {
1149 element->set(key, value);
1150 }
else if (element->contains(key)) {
1151 element->remove(key);
1159 if (element->getType() != other->getType()) {
1163 if (element->getType() == Element::list) {
1166 ElementPtr new_elements = Element::createList();
1167 for (
auto& right : other->listValue()) {
1170 auto f = hierarchy[idx].find(key);
1171 if (f != hierarchy[idx].end()) {
1174 for (
auto& left : element->listValue()) {
1178 if (f->second.match_(mutable_left, mutable_right)) {
1180 mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
1184 new_elements->add(right);
1187 new_elements->add(right);
1191 for (
auto& right : new_elements->listValue()) {
1192 element->add(right);
1197 if (element->getType() == Element::map) {
1198 for (
auto kv : other->mapValue()) {
1199 auto current_key = kv.first;
1200 auto value = boost::const_pointer_cast<
Element>(kv.second);
1201 if (value && value->getType() != Element::null) {
1202 if (element->contains(current_key) &&
1203 (value->getType() == Element::map ||
1204 value->getType() == Element::list)) {
1205 ElementPtr mutable_element = boost::const_pointer_cast<
Element>(element->get(current_key));
1206 mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
1208 element->set(current_key, value);
1220 if (element->getType() != other->getType()) {
1224 if (element->getType() == Element::list) {
1225 for (
auto const& value : other->listValue()) {
1227 for (uint32_t iter = 0; iter < element->listValue().size();) {
1228 bool removed =
false;
1231 auto f = hierarchy[idx].
find(key);
1232 if (f != hierarchy[idx].end()) {
1233 ElementPtr mutable_left = boost::const_pointer_cast<
Element>(element->listValue().at(iter));
1236 if (f->second.match_(mutable_left, mutable_right)) {
1240 if (f->second.no_data_(mutable_right)) {
1244 mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
1245 if (mutable_left->empty()) {
1246 element->remove(iter);
1251 }
else if (element->listValue().at(iter)->equals(*value)) {
1252 element->remove(iter);
1263 if (element->getType() == Element::map) {
1266 ElementPtr new_elements = Element::createMap();
1267 for (
auto kv : other->mapValue()) {
1268 auto current_key = kv.first;
1269 auto value = boost::const_pointer_cast<
Element>(kv.second);
1270 if (value && value->getType() != Element::null) {
1271 if (element->contains(current_key)) {
1272 ElementPtr mutable_element = boost::const_pointer_cast<
Element>(element->get(current_key));
1273 if (mutable_element->getType() == Element::map ||
1274 mutable_element->getType() == Element::list) {
1275 mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
1276 if (mutable_element->empty()) {
1277 element->remove(current_key);
1282 auto f = hierarchy[idx].find(key);
1283 if (f != hierarchy[idx].end()) {
1286 if (f->second.is_key_(current_key)) {
1288 new_elements->set(current_key, mutable_element);
1291 element->remove(current_key);
1297 if (element->size()) {
1298 for (
auto kv : new_elements->mapValue()) {
1299 element->set(kv.first, kv.second);
1308 extend(
const std::string& container,
const std::string& extension,
1310 std::string key,
size_t idx,
bool alter) {
1311 if (element->getType() != other->getType()) {
1315 if (element->getType() == Element::list) {
1316 for (
auto& right : other->listValue()) {
1319 auto f = hierarchy[idx].find(key);
1320 if (f != hierarchy[idx].end()) {
1322 for (
auto& left : element->listValue()) {
1324 if (container == key) {
1327 if (f->second.match_(mutable_left, mutable_right)) {
1328 extend(container, extension, mutable_left, mutable_right,
1329 hierarchy, key, idx, alter);
1337 if (element->getType() == Element::map) {
1338 for (
auto kv : other->mapValue()) {
1339 auto current_key = kv.first;
1340 auto value = boost::const_pointer_cast<
Element>(kv.second);
1341 if (value && value->getType() != Element::null) {
1342 if (element->contains(current_key) &&
1343 (value->getType() == Element::map ||
1344 value->getType() == Element::list)) {
1345 ElementPtr mutable_element = boost::const_pointer_cast<
Element>(element->get(current_key));
1346 if (container == key) {
1349 extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
1350 }
else if (alter && current_key == extension) {
1351 element->set(current_key, value);
1364 int from_type = from->getType();
1365 if (from_type == Element::integer) {
1367 }
else if (from_type == Element::real) {
1369 }
else if (from_type == Element::boolean) {
1371 }
else if (from_type == Element::null) {
1373 }
else if (from_type == Element::string) {
1375 }
else if (from_type == Element::list) {
1377 for (
auto elem : from->listValue()) {
1381 result->add(
copy(elem, level - 1));
1385 }
else if (from_type == Element::map) {
1387 for (
auto kv : from->mapValue()) {
1388 auto key = kv.first;
1389 auto value = kv.second;
1391 result->set(key, value);
1393 result->set(key,
copy(value, level - 1));
1410 "arguments include cycles");
1416 if (a->getType() != b->getType()) {
1419 if (a->getType() == Element::list) {
1422 return (b->empty());
1425 if (a->size() != b->size()) {
1430 const size_t s = a->size();
1431 std::list<ConstElementPtr> l;
1432 for (
size_t i = 0; i < s; ++i) {
1433 l.push_back(b->get(i));
1437 for (
size_t i = 0; i < s; ++i) {
1441 for (
auto it = l.begin(); it != l.end(); ++it) {
1443 if (isEquivalent0(item, *it, level - 1)) {
1460 }
else if (a->getType() == Element::map) {
1462 if (a->size() != b->size()) {
1466 for (
auto kv : a->mapValue()) {
1469 if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1475 return (a->equals(*b));
1483 return (isEquivalent0(a, b, 100));
1488 unsigned indent,
unsigned step) {
1492 if (element->getType() == Element::list) {
1494 if (element->empty()) {
1500 if (!element->get(0)) {
1503 int first_type = element->get(0)->getType();
1504 bool complex =
false;
1505 if ((first_type == Element::list) || (first_type == Element::map)) {
1508 std::string separator = complex ?
",\n" :
", ";
1511 out <<
"[" << (complex ?
"\n" :
" ");
1514 const auto& l = element->listValue();
1515 for (
auto it = l.begin(); it != l.end(); ++it) {
1517 if (it != l.begin()) {
1522 out << std::string(indent + step,
' ');
1530 out <<
"\n" << std::string(indent,
' ');
1535 }
else if (element->getType() == Element::map) {
1537 if (element->size() == 0) {
1546 const auto& m = element->mapValue();
1548 for (
auto it = m.begin(); it != m.end(); ++it) {
1556 out << std::string(indent + step,
' ');
1558 out <<
"\"" << it->first <<
"\": ";
1560 prettyPrint(it->second, out, indent + step, step);
1564 out <<
"\n" << std::string(indent,
' ') <<
"}";
1567 element->toJSON(out);
1573 std::stringstream ss;
1578 void Element::preprocess(std::istream& in, std::stringstream& out) {
1582 while (std::getline(in, line)) {
1585 if (!line.empty() && line[0] ==
'#') {
virtual std::string stringValue() const
bool operator<(Element const &a, Element const &b)
void extend(const std::string &container, const std::string &extension, ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx, bool alter)
Extends data by adding the specified 'extension' elements from 'other' inside the 'container' element...
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 ...
bool operator!=(const Element &a, const Element &b)
virtual int64_t intValue() const
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
boost::shared_ptr< Element > ElementPtr
#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 bool equals(const Element &other) const =0
void mergeDiffDel(ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx)
Merges the diff data by removing the data present in 'other' from 'element' (recursively).
std::vector< FunctionMap > HierarchyDescriptor
Hierarchy descriptor of the containers in a specific Element hierarchy tree.
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...
std::string str() const
Returns a string representing the Element and all its child elements; note that this is different fro...
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...
A generic exception that is thrown when an unexpected error condition occurs.
Notes: IntElement type is changed to int64_t.
boost::shared_ptr< const Element > ConstElementPtr
virtual ConstElementPtr find(const std::string &identifier) const
Recursively finds any data at the given identifier.
virtual size_t size() const
Returns the number of elements in the list.
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-lfc.
std::ostream & operator<<(std::ostream &out, const Element &e)
Insert the Element as a string into stream.
std::string str() const
Returns the position in the textual format.
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
virtual double doubleValue() const
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.
The Element class represents a piece of data, used by the command channel and configuration parts...
uint16_t pos_
The position (offset from the beginning) in the buffer where the name starts.
virtual void remove(const int i)
Removes the element at the given position.
void mergeDiffAdd(ElementPtr &element, ElementPtr &other, HierarchyDescriptor &hierarchy, std::string key, size_t idx)
Merges the diff data by adding the missing elements from 'other' to 'element' (recursively).
virtual bool boolValue() const
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.