25#include <boost/lexical_cast.hpp>
34const char*
const WHITESPACE =
" \b\f\n\r\t";
42 std::ostringstream ss;
204throwJSONError(
const std::string& error,
const std::string& file,
int line,
206 std::stringstream ss;
207 ss << error <<
" in " + file +
":" << line <<
":" << pos;
214 return (out << e.str());
219 return (a.equals(b));
223 return (!a.equals(b));
228 if (a.getType() != b.
getType()) {
231 switch (a.getType()) {
239 return std::strcmp(a.stringValue().c_str(), b.
stringValue().c_str()) < 0;
241 isc_throw(
BadValue,
"cannot compare Elements of type " << to_string(a.getType()));
265 return (
create(
static_cast<long long int>(i), pos));
270 return (
create(
static_cast<long long int>(i), pos));
275 return (
create(
static_cast<long long int>(i), pos));
295 return (
create(std::string(s), pos));
314charIn(
const int c,
const char* chars) {
315 const size_t chars_len = std::strlen(chars);
316 for (
size_t i = 0; i < chars_len; ++i) {
325skipChars(std::istream& in,
const char* chars,
int& line,
int& pos) {
327 while (charIn(c, chars) && c != EOF) {
345skipTo(std::istream& in,
const std::string& file,
int& line,
int& pos,
346 const char* chars,
const char* may_skip=
"") {
354 if (charIn(c, may_skip)) {
357 }
else if (charIn(c, chars)) {
358 while (charIn(in.peek(), may_skip)) {
359 if (in.peek() ==
'\n') {
369 throwJSONError(std::string(
"'") + std::string(1, c) +
"' read, one of \"" + chars +
"\" expected", file, line, pos);
372 throwJSONError(std::string(
"EOF read, one of \"") + chars +
"\" expected", file, line, pos);
379strFromStringstream(std::istream& in,
const std::string& file,
380 const int line,
int& pos) {
381 std::stringstream ss;
388 throwJSONError(
"String expected", file, line, pos);
391 while (c != EOF && c !=
'"') {
426 throwJSONError(
"Unsupported unicode escape", file, line, pos);
433 throwJSONError(
"Unsupported unicode escape", file, line, pos - 2);
439 if ((d >=
'0') && (d <=
'9')) {
441 }
else if ((d >=
'A') && (d <=
'F')) {
442 c = (d -
'A' + 10) << 4;
443 }
else if ((d >=
'a') && (d <=
'f')) {
444 c = (d -
'a' + 10) << 4;
446 throwJSONError(
"Not hexadecimal in unicode escape", file, line, pos - 3);
452 if ((d >=
'0') && (d <=
'9')) {
454 }
else if ((d >=
'A') && (d <=
'F')) {
456 }
else if ((d >=
'a') && (d <=
'f')) {
459 throwJSONError(
"Not hexadecimal in unicode escape", file, line, pos - 4);
463 throwJSONError(
"Bad escape", file, line, pos);
474 throwJSONError(
"Unterminated string", file, line, pos);
480wordFromStringstream(std::istream& in,
int& pos) {
481 std::stringstream ss;
482 while (isalpha(in.peek())) {
483 ss << (char) in.get();
485 pos += ss.str().size();
490numberFromStringstream(std::istream& in,
int& pos) {
491 std::stringstream ss;
492 while (isdigit(in.peek()) || in.peek() ==
'+' || in.peek() ==
'-' ||
493 in.peek() ==
'.' || in.peek() ==
'e' || in.peek() ==
'E') {
494 ss << (char) in.get();
496 pos += ss.str().size();
511fromStringstreamNumber(std::istream& in,
const std::string& file,
512 const int line,
int& pos) {
515 const uint32_t start_pos = pos;
517 const std::string number = numberFromStringstream(in, pos);
520 if (number.find_first_of(
".eE") < number.size()) {
523 Element::Position(file, line, start_pos)));
524 }
catch (
const boost::bad_lexical_cast& exception) {
525 throwJSONError(
"Number overflow while trying to cast '" + number +
526 "' to double: " + exception.what(),
527 file, line, start_pos);
534 Element::Position(file, line, start_pos)));
535 }
catch (
const boost::bad_lexical_cast& exception64) {
539 Element::Position(file, line, start_pos)));
540 }
catch (overflow_error
const& exception128) {
541 throwJSONError(
"Number overflow while trying to cast '" + number +
542 "' to int64 and subsequently to int128: " +
543 exception64.what() +
", " + exception128.what(),
544 file, line, start_pos);
551fromStringstreamBool(std::istream& in,
const std::string& file,
552 const int line,
int& pos) {
555 const uint32_t start_pos = pos;
557 const std::string word = wordFromStringstream(in, pos);
559 if (word ==
"true") {
562 }
else if (word ==
"false") {
566 throwJSONError(std::string(
"Bad boolean value: ") + word, file,
573fromStringstreamNull(std::istream& in,
const std::string& file,
574 const int line,
int& pos) {
577 const uint32_t start_pos = pos;
579 const std::string word = wordFromStringstream(in, pos);
580 if (word ==
"null") {
583 throwJSONError(std::string(
"Bad null value: ") + word, file,
590fromStringstreamString(std::istream& in,
const std::string& file,
int& line,
594 const uint32_t start_pos = pos;
596 const std::string string_value = strFromStringstream(in, file, line, pos);
602fromStringstreamList(std::istream& in,
const std::string& file,
int& line,
606 ElementPtr cur_list_element;
608 skipChars(in, WHITESPACE, line, pos);
609 while (c != EOF && c !=
']') {
610 if (in.peek() !=
']') {
612 list->add(cur_list_element);
613 c = skipTo(in, file, line, pos,
",]", WHITESPACE);
623fromStringstreamMap(std::istream& in,
const std::string& file,
int& line,
626 skipChars(in, WHITESPACE, line, pos);
629 throwJSONError(std::string(
"Unterminated map, <string> or } expected"), file, line, pos);
630 }
else if (c ==
'}') {
634 while (c != EOF && c !=
'}') {
635 std::string key = strFromStringstream(in, file, line, pos);
637 skipTo(in, file, line, pos,
":", WHITESPACE);
641 map->set(key, value);
643 c = skipTo(in, file, line, pos,
",}", WHITESPACE);
654 return (std::string(
"integer"));
656 return (std::string(
"bigint"));
658 return (std::string(
"real"));
660 return (std::string(
"boolean"));
662 return (std::string(
"string"));
664 return (std::string(
"list"));
666 return (std::string(
"map"));
668 return (std::string(
"null"));
670 return (std::string(
"any"));
672 return (std::string(
"unknown"));
678 if (type_name ==
"integer") {
680 }
else if (type_name ==
"bigint") {
682 }
else if (type_name ==
"real") {
684 }
else if (type_name ==
"boolean") {
686 }
else if (type_name ==
"string") {
688 }
else if (type_name ==
"list") {
690 }
else if (type_name ==
"map") {
692 }
else if (type_name ==
"named_set") {
694 }
else if (type_name ==
"null") {
696 }
else if (type_name ==
"any") {
706 int line = 1, pos = 1;
707 stringstream filtered;
719 int line = 1, pos = 1;
720 stringstream filtered;
724 return (
fromJSON(preproc ? filtered : in, file_name, line, pos));
732 bool el_read =
false;
733 skipChars(in, WHITESPACE, line, pos);
734 while (c != EOF && !el_read) {
753 element = fromStringstreamNumber(in, file, line, pos);
760 element = fromStringstreamBool(in, file, line, pos);
766 element = fromStringstreamNull(in, file, line, pos);
772 element = fromStringstreamString(in, file, line, pos);
776 element = fromStringstreamList(in, file, line, pos);
780 element = fromStringstreamMap(in, file, line, pos);
786 throwJSONError(std::string(
"error: unexpected character ") + std::string(1, c), file, line, pos);
799 std::stringstream ss;
802 int line = 1, pos = 1;
803 stringstream filtered;
808 skipChars(ss, WHITESPACE, line, pos);
810 if (ss.peek() != EOF) {
811 throwJSONError(
"Extra data",
"<string>", line, pos);
821 std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
822 if (!infile.is_open()) {
823 const char* error = strerror(errno);
828 return (
fromJSON(infile, file_name, preproc));
851 ostringstream val_ss;
854 if (val_ss.str().find_first_of(
'.') == string::npos) {
877 for (
size_t i = 0; i <
str.size(); ++i) {
878 const signed char c =
str[i];
905 if (c < 0x20 || c == 0x7f) {
906 std::ostringstream esc;
911 << (
static_cast<unsigned>(c) & 0xff);
925 const std::vector<ElementPtr>& v =
listValue();
927 for (
auto const& it : v) {
943 for (
auto const& it : m) {
949 ss <<
"\"" << it.first <<
"\": ";
951 it.second->toJSON(ss);
965 const size_t sep =
id.find(
'/');
966 if (sep == std::string::npos) {
972 if (sep + 1 !=
id.
size()) {
973 return (ce->find(
id.substr(sep + 1)));
985 std::stringstream ss;
987 int line = 0, pos = 0;
988 return (
fromJSON(ss,
"<wire>", line, pos));
1003 int line = 0, pos = 0;
1004 return (
fromJSON(in,
"<wire>", line, pos));
1044 return (other.getType() ==
Element::bigint && i_ == other.bigIntValue()) ||
1051 (fabs(d - other.doubleValue()) < 1e-14);
1057 (b == other.boolValue());
1068 (s == other.stringValue());
1074 const size_t s =
size();
1075 if (s != other.size()) {
1078 for (
size_t i = 0; i < s; ++i) {
1095 int const t(l.at(0)->getType());
1098 if (index.empty()) {
1109 }
else if (t == list) {
1114 if (!index.empty()) {
1122 std::sort(l.begin(), l.end(), comparator);
1128 if (
size() != other.size()) {
1131 for (
auto const& kv :
mapValue()) {
1132 auto key = kv.first;
1133 if (other.contains(key)) {
1134 if (!
get(key)->equals(*other.get(key))) {
1165 for (
auto const& kv : b->mapValue()) {
1166 auto key = kv.first;
1167 if (a->contains(key)) {
1168 if (a->get(key)->equals(*b->get(key))) {
1187 for (
auto const& kv : a->mapValue()) {
1188 auto key = kv.first;
1189 if (!b->contains(key) ||
1190 !a->get(key)->equals(*b->get(key))) {
1191 result->set(key, kv.second);
1205 for (
auto const& kv : other->mapValue()) {
1206 auto key = kv.first;
1207 auto value = kv.second;
1209 element->set(key, value);
1210 }
else if (element->contains(key)) {
1211 element->remove(key);
1219 if (element->getType() != other->getType()) {
1227 for (
auto const& right : other->listValue()) {
1230 auto f = hierarchy[idx].find(key);
1231 if (f != hierarchy[idx].end()) {
1233 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1234 for (
auto const& left : element->listValue()) {
1235 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1238 if (f->second.match_(mutable_left, mutable_right)) {
1240 mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
1244 new_elements->add(right);
1247 new_elements->add(right);
1251 for (
auto const& right : new_elements->listValue()) {
1252 element->add(right);
1258 for (
auto const& kv : other->mapValue()) {
1259 auto current_key = kv.first;
1260 auto value = boost::const_pointer_cast<Element>(kv.second);
1262 if (element->contains(current_key) &&
1265 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1266 mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
1268 element->set(current_key, value);
1280 if (element->getType() != other->getType()) {
1285 for (
auto const& value : other->listValue()) {
1286 ElementPtr mutable_right = boost::const_pointer_cast<Element>(value);
1287 for (uint32_t iter = 0; iter < element->listValue().
size();) {
1288 bool removed =
false;
1291 auto f = hierarchy[idx].find(key);
1292 if (f != hierarchy[idx].end()) {
1293 ElementPtr mutable_left = boost::const_pointer_cast<Element>(element->listValue().at(iter));
1296 if (f->second.match_(mutable_left, mutable_right)) {
1300 if (f->second.no_data_(mutable_right)) {
1301 element->remove(iter);
1304 mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
1305 if (mutable_left->empty()) {
1306 element->remove(iter);
1311 }
else if (element->listValue().at(iter)->equals(*value)) {
1312 element->remove(iter);
1327 for (
auto const& kv : other->mapValue()) {
1328 auto current_key = kv.first;
1329 auto value = boost::const_pointer_cast<Element>(kv.second);
1331 if (element->contains(current_key)) {
1332 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1335 mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
1336 if (mutable_element->empty()) {
1337 element->remove(current_key);
1342 auto f = hierarchy[idx].find(key);
1343 if (f != hierarchy[idx].end()) {
1346 if (f->second.is_key_(current_key)) {
1348 new_elements->set(current_key, mutable_element);
1351 element->remove(current_key);
1357 if (element->size()) {
1358 for (
auto const& kv : new_elements->mapValue()) {
1359 element->set(kv.first, kv.second);
1368extend(
const std::string& container,
const std::string& extension,
1370 std::string key,
size_t idx,
bool alter) {
1371 if (element->getType() != other->getType()) {
1376 for (
auto const& right : other->listValue()) {
1379 auto f = hierarchy[idx].find(key);
1380 if (f != hierarchy[idx].end()) {
1381 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1382 for (
auto const& left : element->listValue()) {
1383 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1384 if (container == key) {
1387 if (f->second.match_(mutable_left, mutable_right)) {
1388 extend(container, extension, mutable_left, mutable_right,
1389 hierarchy, key, idx, alter);
1398 for (
auto const& kv : other->mapValue()) {
1399 auto current_key = kv.first;
1400 auto value = boost::const_pointer_cast<Element>(kv.second);
1402 if (element->contains(current_key) &&
1405 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1406 if (container == key) {
1409 extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
1410 }
else if (alter && current_key == extension) {
1411 element->set(current_key, value);
1424 int from_type = from->getType();
1437 for (
auto const& elem : from->listValue()) {
1441 result->add(
copy(elem, level - 1));
1447 for (
auto const& kv : from->mapValue()) {
1448 auto key = kv.first;
1449 auto value = kv.second;
1451 result->set(key, value);
1453 result->set(key,
copy(value, level - 1));
1466isEquivalent0(ConstElementPtr a, ConstElementPtr b,
unsigned level) {
1470 "arguments include cycles");
1473 isc_throw(BadValue,
"isEquivalent got a null pointer");
1476 if (a->getType() != b->getType()) {
1482 return (b->empty());
1485 if (a->size() != b->size()) {
1490 const size_t s = a->size();
1491 std::list<ConstElementPtr> l;
1492 for (
size_t i = 0; i < s; ++i) {
1493 l.push_back(b->get(i));
1497 for (
size_t i = 0; i < s; ++i) {
1498 ConstElementPtr item = a->get(i);
1501 for (
auto it = l.begin(); it != l.end(); ++it) {
1503 if (isEquivalent0(item, *it, level - 1)) {
1517 isc_throw(Unexpected,
"isEquivalent internal error");
1522 if (a->size() != b->size()) {
1526 for (
auto const& kv : a->
mapValue()) {
1528 ConstElementPtr item = b->get(kv.first);
1529 if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1535 return (a->equals(*b));
1543 return (isEquivalent0(a, b, 100));
1548 unsigned indent,
unsigned step) {
1554 if (element->empty()) {
1560 if (!element->get(0)) {
1563 int first_type = element->get(0)->getType();
1564 bool complex =
false;
1568 std::string separator = complex ?
",\n" :
", ";
1571 out <<
"[" << (complex ?
"\n" :
" ");
1574 auto const& l = element->listValue();
1576 for (
auto const& it : l) {
1585 out << std::string(indent + step,
' ');
1593 out <<
"\n" << std::string(indent,
' ');
1600 if (element->size() == 0) {
1609 auto const& m = element->mapValue();
1611 for (
auto const& it : m) {
1619 out << std::string(indent + step,
' ');
1621 out <<
"\"" << it.first <<
"\": ";
1627 out <<
"\n" << std::string(indent,
' ') <<
"}";
1630 element->toJSON(out);
1636 std::stringstream ss;
1645 while (std::getline(in, line)) {
1648 if (!line.empty() && line[0] ==
'#') {
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
bool equals(const Element &other) const override
Checks whether the other Element is equal.
void toJSON(std::ostream &ss) const override
Converts the Element to JSON format and appends it to the given stringstream.
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
bool equals(const Element &other) const
bool equals(const Element &other) const
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
The Element class represents a piece of data, used by the command channel and configuration parts.
static ElementPtr create(const Position &pos=ZERO_POSITION())
virtual bool equals(const Element &other) const =0
virtual bool getValue(int64_t &t) const
static std::string typeToName(Element::types type)
Returns the name of the given type as a string.
virtual int64_t intValue() const
std::string str() const
Returns a string representing the Element and all its child elements; note that this is different fro...
virtual std::string stringValue() const
std::string toWire() const
Returns the wireformat for the Element and all its child elements.
static ElementPtr fromWire(std::stringstream &in, int length)
These function pparse the wireformat at the given stringstream (of the given length).
virtual bool setValue(const long long int v)
static ElementPtr fromJSONFile(const std::string &file_name, bool preproc=false)
Reads contents of specified file and interprets it as JSON.
virtual bool empty() const
Return true if there are no elements in the list.
virtual void remove(const int i)
Removes the element at the given position.
virtual bool contains(const std::string &name) const
Checks if there is data at the given key.
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.
virtual const std::map< std::string, ConstElementPtr > & mapValue() const
virtual void add(ElementPtr element)
Adds an ElementPtr to the list.
virtual const std::vector< ElementPtr > & listValue() const
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
virtual ConstElementPtr get(const int i) const
Returns the ElementPtr at the given index.
types
The types that an Element can hold.
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
static Element::types nameToType(const std::string &type_name)
Converts the string to the corresponding type Throws a TypeError if the name is unknown.
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
virtual void toJSON(std::ostream &ss) const =0
Converts the Element to JSON format and appends it to the given stringstream.
virtual void set(const size_t i, ElementPtr element)
Sets the ElementPtr at the given index.
virtual double doubleValue() const
virtual isc::util::int128_t bigIntValue() const
virtual bool boolValue() const
static void preprocess(std::istream &in, std::stringstream &out)
input text preprocessor
virtual ElementPtr getNonConst(const int i) const
returns element as non-const pointer
Notes: IntElement type is changed to int64_t.
bool equals(const Element &other) const
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
void sort(std::string const &index=std::string())
Sorts the elements inside the list.
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
bool equals(const Element &other) const
ConstElementPtr find(const std::string &id) const override
Recursively finds any data at the given identifier.
void set(const std::string &key, ConstElementPtr value) override
Sets the ElementPtr at the given key.
bool equals(const Element &other) const override
void toJSON(std::ostream &ss) const override
Converts the Element to JSON format and appends it to the given stringstream.
bool equals(const Element &other) const
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
void toJSON(std::ostream &ss) const
Converts the Element to JSON format and appends it to the given stringstream.
bool equals(const Element &other) const
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
#define throwTypeError(error)
Add the position to a TypeError message should be used in place of isc_throw(TypeError,...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
bool operator==(const Element &a, const Element &b)
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).
void removeIdentical(ElementPtr a, ConstElementPtr b)
Remove all values from the first ElementPtr that are equal in the second.
void merge(ElementPtr element, ConstElementPtr other)
Merges the data from other into element.
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).
bool operator<(Element const &a, Element const &b)
bool isEquivalent(ConstElementPtr a, ConstElementPtr b)
Compares the data with other using unordered lists.
void prettyPrint(ConstElementPtr element, std::ostream &out, unsigned indent, unsigned step)
Pretty prints the data into stream.
boost::shared_ptr< const Element > ConstElementPtr
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...
bool isNull(ConstElementPtr p)
Checks whether the given ElementPtr is a NULL pointer.
std::ostream & operator<<(std::ostream &out, const Element::Position &pos)
Insert Element::Position as a string into stream.
bool operator!=(const Element &a, const Element &b)
boost::shared_ptr< Element > ElementPtr
std::vector< FunctionMap > HierarchyDescriptor
Hierarchy descriptor of the containers in a specific Element hierarchy tree.
boost::multiprecision::checked_int128_t int128_t
Defines the logger used by the top-level component of kea-lfc.
Represents the position of the data element within a configuration string.
uint32_t pos_
Position within the line.
std::string str() const
Returns the position in the textual format.
uint32_t line_
Line number.
std::string file_
File name.