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());
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,
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 char c =
str[i];
905 if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
906 std::ostringstream esc;
911 << (
static_cast<unsigned>(c) & 0xff);
925 const std::vector<ElementPtr>& v =
listValue();
926 for (
auto it = v.begin(); it != v.end(); ++it) {
927 if (it != v.begin()) {
939 for (
auto it = m.begin(); it != m.end(); ++it) {
940 if (it != m.begin()) {
943 ss <<
"\"" << (*it).first <<
"\": ";
945 (*it).second->toJSON(ss);
959 const size_t sep =
id.find(
'/');
960 if (sep == std::string::npos) {
966 if (sep + 1 !=
id.
size()) {
967 return (ce->find(
id.substr(sep + 1)));
979 std::stringstream ss;
981 int line = 0, pos = 0;
982 return (
fromJSON(ss,
"<wire>", line, pos));
997 int line = 0, pos = 0;
998 return (
fromJSON(in,
"<wire>", line, pos));
1068 const size_t s =
size();
1069 if (s != other.
size()) {
1072 for (
size_t i = 0; i < s; ++i) {
1089 int const t(l.at(0)->getType());
1092 if (index.empty()) {
1103 }
else if (t ==
list) {
1108 if (!index.empty()) {
1116 std::sort(l.begin(), l.end(), comparator);
1126 auto key = kv.first;
1159 for (
auto kv : b->mapValue()) {
1160 auto key = kv.first;
1161 if (a->contains(key)) {
1162 if (a->get(key)->equals(*b->get(key))) {
1181 for (
auto kv : a->mapValue()) {
1182 auto key = kv.first;
1183 if (!b->contains(key) ||
1184 !a->get(key)->equals(*b->get(key))) {
1185 result->set(key, kv.second);
1199 for (
auto kv : other->mapValue()) {
1200 auto key = kv.first;
1201 auto value = kv.second;
1203 element->set(key, value);
1204 }
else if (element->contains(key)) {
1205 element->remove(key);
1213 if (element->getType() != other->getType()) {
1221 for (
auto& right : other->listValue()) {
1224 auto f = hierarchy[idx].find(key);
1225 if (f != hierarchy[idx].end()) {
1227 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1228 for (
auto& left : element->listValue()) {
1229 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1232 if (f->second.match_(mutable_left, mutable_right)) {
1234 mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
1238 new_elements->add(right);
1241 new_elements->add(right);
1245 for (
auto& right : new_elements->listValue()) {
1246 element->add(right);
1252 for (
auto kv : other->mapValue()) {
1253 auto current_key = kv.first;
1254 auto value = boost::const_pointer_cast<Element>(kv.second);
1256 if (element->contains(current_key) &&
1259 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1260 mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
1262 element->set(current_key, value);
1274 if (element->getType() != other->getType()) {
1279 for (
auto const& value : other->listValue()) {
1280 ElementPtr mutable_right = boost::const_pointer_cast<Element>(value);
1281 for (uint32_t iter = 0; iter < element->listValue().
size();) {
1282 bool removed =
false;
1285 auto f = hierarchy[idx].find(key);
1286 if (f != hierarchy[idx].end()) {
1287 ElementPtr mutable_left = boost::const_pointer_cast<Element>(element->listValue().at(iter));
1290 if (f->second.match_(mutable_left, mutable_right)) {
1294 if (f->second.no_data_(mutable_right)) {
1295 element->remove(iter);
1298 mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
1299 if (mutable_left->empty()) {
1300 element->remove(iter);
1305 }
else if (element->listValue().at(iter)->equals(*value)) {
1306 element->remove(iter);
1321 for (
auto kv : other->mapValue()) {
1322 auto current_key = kv.first;
1323 auto value = boost::const_pointer_cast<Element>(kv.second);
1325 if (element->contains(current_key)) {
1326 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1329 mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
1330 if (mutable_element->empty()) {
1331 element->remove(current_key);
1336 auto f = hierarchy[idx].find(key);
1337 if (f != hierarchy[idx].end()) {
1340 if (f->second.is_key_(current_key)) {
1342 new_elements->set(current_key, mutable_element);
1345 element->remove(current_key);
1351 if (element->size()) {
1352 for (
auto kv : new_elements->mapValue()) {
1353 element->set(kv.first, kv.second);
1362extend(
const std::string& container,
const std::string& extension,
1364 std::string key,
size_t idx,
bool alter) {
1365 if (element->getType() != other->getType()) {
1370 for (
auto& right : other->listValue()) {
1373 auto f = hierarchy[idx].find(key);
1374 if (f != hierarchy[idx].end()) {
1375 ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
1376 for (
auto& left : element->listValue()) {
1377 ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
1378 if (container == key) {
1381 if (f->second.match_(mutable_left, mutable_right)) {
1382 extend(container, extension, mutable_left, mutable_right,
1383 hierarchy, key, idx, alter);
1392 for (
auto kv : other->mapValue()) {
1393 auto current_key = kv.first;
1394 auto value = boost::const_pointer_cast<Element>(kv.second);
1396 if (element->contains(current_key) &&
1399 ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
1400 if (container == key) {
1403 extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
1404 }
else if (alter && current_key == extension) {
1405 element->set(current_key, value);
1418 int from_type = from->getType();
1431 for (
auto elem : from->listValue()) {
1435 result->add(
copy(elem, level - 1));
1441 for (
auto kv : from->mapValue()) {
1442 auto key = kv.first;
1443 auto value = kv.second;
1445 result->set(key, value);
1447 result->set(key,
copy(value, level - 1));
1464 "arguments include cycles");
1467 isc_throw(BadValue,
"isEquivalent got a null pointer");
1470 if (a->getType() != b->getType()) {
1476 return (b->empty());
1479 if (a->size() != b->size()) {
1484 const size_t s = a->size();
1485 std::list<ConstElementPtr> l;
1486 for (
size_t i = 0; i < s; ++i) {
1487 l.push_back(b->get(i));
1491 for (
size_t i = 0; i < s; ++i) {
1495 for (
auto it = l.begin(); it != l.end(); ++it) {
1497 if (isEquivalent0(item, *it, level - 1)) {
1511 isc_throw(Unexpected,
"isEquivalent internal error");
1516 if (a->size() != b->size()) {
1520 for (
auto kv : a->mapValue()) {
1523 if (!item || !isEquivalent0(kv.second, item, level - 1)) {
1529 return (a->equals(*b));
1537 return (isEquivalent0(a, b, 100));
1542 unsigned indent,
unsigned step) {
1548 if (element->empty()) {
1554 if (!element->get(0)) {
1557 int first_type = element->get(0)->getType();
1558 bool complex =
false;
1562 std::string separator = complex ?
",\n" :
", ";
1565 out <<
"[" << (complex ?
"\n" :
" ");
1568 const auto& l = element->listValue();
1569 for (
auto it = l.begin(); it != l.end(); ++it) {
1571 if (it != l.begin()) {
1576 out << std::string(indent + step,
' ');
1584 out <<
"\n" << std::string(indent,
' ');
1591 if (element->size() == 0) {
1600 const auto& m = element->mapValue();
1602 for (
auto it = m.begin(); it != m.end(); ++it) {
1610 out << std::string(indent + step,
' ');
1612 out <<
"\"" << it->first <<
"\": ";
1614 prettyPrint(it->second, out, indent + step, step);
1618 out <<
"\n" << std::string(indent,
' ') <<
"}";
1621 element->toJSON(out);
1627 std::stringstream ss;
1636 while (std::getline(in, line)) {
1639 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
types
The types that an Element can hold.
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.
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.