2023-04-05 16:07:38 +03:00
|
|
|
// Copyright (C) 2010-2023 Internet Systems Consortium, Inc. ("ISC")
|
2010-02-02 14:14:52 +00:00
|
|
|
//
|
2015-12-15 21:37:34 +01:00
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
2010-02-02 14:14:52 +00:00
|
|
|
|
2010-07-16 06:31:56 +00:00
|
|
|
#include <config.h>
|
2010-03-19 19:58:38 +00:00
|
|
|
|
2010-07-16 06:31:56 +00:00
|
|
|
#include <cc/data.h>
|
2023-10-02 15:07:39 +03:00
|
|
|
#include <util/bigints.h>
|
2009-10-30 17:28:17 +00:00
|
|
|
|
2013-01-08 20:18:06 -08:00
|
|
|
#include <cstring>
|
2010-04-01 00:38:56 +00:00
|
|
|
#include <cassert>
|
2010-06-30 19:31:23 +00:00
|
|
|
#include <climits>
|
2017-03-05 09:03:09 +01:00
|
|
|
#include <list>
|
2010-08-16 23:36:54 +00:00
|
|
|
#include <map>
|
2009-10-30 17:28:17 +00:00
|
|
|
#include <cstdio>
|
|
|
|
#include <iostream>
|
2016-12-11 18:47:58 +01:00
|
|
|
#include <iomanip>
|
2010-03-15 04:52:16 +00:00
|
|
|
#include <string>
|
2009-10-30 17:28:17 +00:00
|
|
|
#include <sstream>
|
2014-05-08 17:25:26 +02:00
|
|
|
#include <fstream>
|
2013-06-11 09:35:08 +09:00
|
|
|
#include <cerrno>
|
2009-10-30 17:28:17 +00:00
|
|
|
|
2013-07-08 11:43:54 +09:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2009-10-30 17:28:17 +00:00
|
|
|
|
2010-06-15 13:37:41 +00:00
|
|
|
#include <cmath>
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2023-10-02 15:07:39 +03:00
|
|
|
using isc::util::int128_t;
|
|
|
|
|
2012-04-02 15:16:27 +02:00
|
|
|
namespace {
|
2013-01-08 20:12:37 -08:00
|
|
|
const char* const WHITESPACE = " \b\f\n\r\t";
|
2012-04-02 15:16:27 +02:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
namespace isc {
|
|
|
|
namespace data {
|
|
|
|
|
2014-04-22 16:40:29 +02:00
|
|
|
std::string
|
|
|
|
Element::Position::str() const {
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << file_ << ":" << line_ << ":" << pos_;
|
|
|
|
return (ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& out, const Element::Position& pos) {
|
|
|
|
out << pos.str();
|
|
|
|
return (out);
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
std::string
|
2010-08-16 23:36:54 +00:00
|
|
|
Element::str() const {
|
2010-06-15 10:11:48 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
toJSON(ss);
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ss.str());
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
2010-08-16 23:36:54 +00:00
|
|
|
Element::toWire() const {
|
2010-06-15 10:11:48 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
toJSON(ss);
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ss.str());
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
Element::toWire(std::ostream& ss) const {
|
2010-06-15 10:11:48 +00:00
|
|
|
toJSON(ss);
|
|
|
|
}
|
|
|
|
|
2010-03-19 19:58:38 +00:00
|
|
|
bool
|
2013-07-03 17:08:12 +09:00
|
|
|
Element::getValue(int64_t&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2012-10-10 03:51:09 +05:30
|
|
|
Element::getValue(double&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2012-10-10 03:51:09 +05:30
|
|
|
Element::getValue(bool&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2012-10-10 03:51:09 +05:30
|
|
|
Element::getValue(std::string&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2016-11-24 14:44:00 +01:00
|
|
|
Element::getValue(std::vector<ElementPtr>&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2012-10-10 03:51:09 +05:30
|
|
|
Element::getValue(std::map<std::string, ConstElementPtr>&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-07-08 11:43:54 +09:00
|
|
|
Element::setValue(const long long int) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
2023-04-26 15:44:33 +03:00
|
|
|
bool
|
|
|
|
Element::setValue(isc::util::int128_t const&) {
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
|
2010-03-19 19:58:38 +00:00
|
|
|
bool
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::setValue(const double) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::setValue(const bool) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::setValue(const std::string&) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2016-11-24 14:44:00 +01:00
|
|
|
Element::setValue(const std::vector<ElementPtr>&) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::setValue(const std::map<std::string, ConstElementPtr>&) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::get(const int) const {
|
2021-07-15 15:14:20 +03:00
|
|
|
throwTypeError("get(int) called on a non-container Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
2016-11-24 14:44:00 +01:00
|
|
|
ElementPtr
|
2016-12-22 19:35:04 +01:00
|
|
|
Element::getNonConst(const int) const {
|
2021-07-22 20:18:24 +03:00
|
|
|
throwTypeError("get(int) called on a non-container Element");
|
2016-11-24 14:44:00 +01:00
|
|
|
}
|
|
|
|
|
2010-03-19 19:58:38 +00:00
|
|
|
void
|
2016-11-24 14:44:00 +01:00
|
|
|
Element::set(const size_t, ElementPtr) {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("set(int, element) called on a non-list Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-11-24 14:44:00 +01:00
|
|
|
Element::add(ElementPtr) {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("add() called on a non-list Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::remove(const int) {
|
2021-07-22 20:18:24 +03:00
|
|
|
throwTypeError("remove(int) called on a non-container Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
2010-08-16 23:36:54 +00:00
|
|
|
Element::size() const {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("size() called on a non-list Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
2013-10-10 14:59:55 +02:00
|
|
|
bool
|
|
|
|
Element::empty() const {
|
2021-07-15 15:14:20 +03:00
|
|
|
throwTypeError("empty() called on a non-container Element");
|
2013-10-10 14:59:55 +02:00
|
|
|
}
|
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::get(const std::string&) const {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("get(string) called on a non-map Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::set(const std::string&, ConstElementPtr) {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("set(name, element) called on a non-map Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::remove(const std::string&) {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("remove(string) called on a non-map Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::contains(const std::string&) const {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("contains(string) called on a non-map Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr
|
2010-11-08 05:58:10 +00:00
|
|
|
Element::find(const std::string&) const {
|
2015-05-21 15:35:04 +02:00
|
|
|
throwTypeError("find(string) called on a non-map Element");
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2012-10-10 03:51:09 +05:30
|
|
|
Element::find(const std::string&, ConstElementPtr&) const {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-03-19 19:58:38 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
namespace {
|
|
|
|
inline void
|
2010-10-15 08:59:24 +00:00
|
|
|
throwJSONError(const std::string& error, const std::string& file, int line,
|
2020-04-17 17:11:56 +03:00
|
|
|
int pos) {
|
2010-06-16 10:15:21 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
ss << error << " in " + file + ":" << line << ":" << pos;
|
|
|
|
isc_throw(JSONError, ss.str());
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2020-04-17 14:46:42 +03:00
|
|
|
} // end anonymous namespace
|
2009-10-30 17:28:17 +00:00
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
std::ostream&
|
2013-01-08 20:12:37 -08:00
|
|
|
operator<<(std::ostream& out, const Element& e) {
|
2010-08-16 23:36:54 +00:00
|
|
|
return (out << e.str());
|
2009-11-04 13:41:12 +00:00
|
|
|
}
|
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
bool
|
|
|
|
operator==(const Element& a, const Element& b) {
|
|
|
|
return (a.equals(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const Element& a, const Element& b) {
|
|
|
|
return (!a.equals(b));
|
2020-04-17 12:32:39 +03:00
|
|
|
}
|
2010-02-28 20:00:49 +00:00
|
|
|
|
2021-07-15 15:14:20 +03:00
|
|
|
bool
|
|
|
|
operator<(Element const& a, Element const& b) {
|
|
|
|
if (a.getType() != b.getType()) {
|
|
|
|
isc_throw(BadValue, "cannot compare Elements of different types");
|
|
|
|
}
|
|
|
|
switch (a.getType()) {
|
|
|
|
case Element::integer:
|
|
|
|
return a.intValue() < b.intValue();
|
|
|
|
case Element::real:
|
|
|
|
return a.doubleValue() < b.doubleValue();
|
|
|
|
case Element::boolean:
|
|
|
|
return b.boolValue() || !a.boolValue();
|
|
|
|
case Element::string:
|
|
|
|
return std::strcmp(a.stringValue().c_str(), b.stringValue().c_str()) < 0;
|
2023-04-05 16:07:38 +03:00
|
|
|
default:
|
|
|
|
isc_throw(BadValue, "cannot compare Elements of type " << to_string(a.getType()));
|
2021-07-15 15:14:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
//
|
|
|
|
// factory functions
|
|
|
|
//
|
2010-06-15 10:11:48 +00:00
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
Element::create(const Position& pos) {
|
|
|
|
return (ElementPtr(new NullElement(pos)));
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
Element::create(const long long int i, const Position& pos) {
|
|
|
|
return (ElementPtr(new IntElement(static_cast<int64_t>(i), pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2023-04-26 15:44:33 +03:00
|
|
|
ElementPtr
|
|
|
|
Element::create(const isc::util::int128_t& i, const Position& pos) {
|
|
|
|
return (ElementPtr(new BigIntElement(i, pos)));
|
|
|
|
}
|
|
|
|
|
2014-04-18 18:11:48 +02:00
|
|
|
ElementPtr
|
|
|
|
Element::create(const int i, const Position& pos) {
|
|
|
|
return (create(static_cast<long long int>(i), pos));
|
2020-04-17 12:32:39 +03:00
|
|
|
}
|
2014-04-18 18:11:48 +02:00
|
|
|
|
|
|
|
ElementPtr
|
|
|
|
Element::create(const long int i, const Position& pos) {
|
|
|
|
return (create(static_cast<long long int>(i), pos));
|
2020-04-17 12:32:39 +03:00
|
|
|
}
|
2014-04-18 18:11:48 +02:00
|
|
|
|
2022-06-28 17:56:43 +03:00
|
|
|
ElementPtr
|
|
|
|
Element::create(const uint32_t i, const Position& pos) {
|
|
|
|
return (create(static_cast<long long int>(i), pos));
|
|
|
|
}
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
Element::create(const double d, const Position& pos) {
|
|
|
|
return (ElementPtr(new DoubleElement(d, pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2014-04-18 18:11:48 +02:00
|
|
|
ElementPtr
|
|
|
|
Element::create(const bool b, const Position& pos) {
|
|
|
|
return (ElementPtr(new BoolElement(b, pos)));
|
|
|
|
}
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
Element::create(const std::string& s, const Position& pos) {
|
|
|
|
return (ElementPtr(new StringElement(s, pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2014-04-18 18:11:48 +02:00
|
|
|
Element::create(const char *s, const Position& pos) {
|
|
|
|
return (create(std::string(s), pos));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
Element::createList(const Position& pos) {
|
|
|
|
return (ElementPtr(new ListElement(pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
Element::createMap(const Position& pos) {
|
|
|
|
return (ElementPtr(new MapElement(pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2010-06-15 13:02:26 +00:00
|
|
|
// helper functions for fromJSON factory
|
2009-10-30 17:28:17 +00:00
|
|
|
//
|
2010-03-15 08:35:45 +00:00
|
|
|
namespace {
|
|
|
|
bool
|
2013-01-08 20:12:37 -08:00
|
|
|
charIn(const int c, const char* chars) {
|
2013-01-08 20:18:06 -08:00
|
|
|
const size_t chars_len = std::strlen(chars);
|
|
|
|
for (size_t i = 0; i < chars_len; ++i) {
|
2009-10-30 17:28:17 +00:00
|
|
|
if (chars[i] == c) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (true);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
void
|
2013-01-08 20:12:37 -08:00
|
|
|
skipChars(std::istream& in, const char* chars, int& line, int& pos) {
|
2012-12-21 10:11:05 +01:00
|
|
|
int c = in.peek();
|
2013-01-08 20:12:37 -08:00
|
|
|
while (charIn(c, chars) && c != EOF) {
|
2009-11-04 13:41:12 +00:00
|
|
|
if (c == '\n') {
|
2010-03-15 08:24:50 +00:00
|
|
|
++line;
|
2009-11-04 13:41:12 +00:00
|
|
|
pos = 1;
|
|
|
|
} else {
|
2010-03-15 08:24:50 +00:00
|
|
|
++pos;
|
2009-11-04 13:41:12 +00:00
|
|
|
}
|
2013-02-09 14:52:37 -08:00
|
|
|
in.ignore();
|
2009-10-30 17:28:17 +00:00
|
|
|
c = in.peek();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip on the input stream to one of the characters in chars
|
2013-10-28 00:32:16 -07:00
|
|
|
// if another character is found this function throws JSONError
|
2010-01-16 07:37:40 +00:00
|
|
|
// unless that character is specified in the optional may_skip
|
2009-10-30 17:28:17 +00:00
|
|
|
//
|
2013-10-28 00:32:16 -07:00
|
|
|
// It returns the found character (as an int value).
|
|
|
|
int
|
2020-04-17 17:11:56 +03:00
|
|
|
skipTo(std::istream& in, const std::string& file, int& line, int& pos,
|
|
|
|
const char* chars, const char* may_skip="") {
|
2012-12-21 10:11:05 +01:00
|
|
|
int c = in.get();
|
2010-03-15 08:24:50 +00:00
|
|
|
++pos;
|
2009-10-30 17:28:17 +00:00
|
|
|
while (c != EOF) {
|
2009-11-04 13:41:12 +00:00
|
|
|
if (c == '\n') {
|
|
|
|
pos = 1;
|
2010-03-15 08:24:50 +00:00
|
|
|
++line;
|
2009-11-04 13:41:12 +00:00
|
|
|
}
|
2013-01-08 20:12:37 -08:00
|
|
|
if (charIn(c, may_skip)) {
|
2009-10-30 17:28:17 +00:00
|
|
|
c = in.get();
|
2010-03-15 08:24:50 +00:00
|
|
|
++pos;
|
2013-01-08 20:12:37 -08:00
|
|
|
} else if (charIn(c, chars)) {
|
|
|
|
while (charIn(in.peek(), may_skip)) {
|
2009-11-04 13:41:12 +00:00
|
|
|
if (in.peek() == '\n') {
|
|
|
|
pos = 1;
|
2010-03-15 08:24:50 +00:00
|
|
|
++line;
|
2013-10-28 00:32:16 -07:00
|
|
|
} else {
|
|
|
|
++pos;
|
2009-11-04 13:41:12 +00:00
|
|
|
}
|
2013-02-09 14:52:37 -08:00
|
|
|
in.ignore();
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2013-10-28 00:32:16 -07:00
|
|
|
return (c);
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2012-12-21 10:11:05 +01:00
|
|
|
throwJSONError(std::string("'") + std::string(1, c) + "' read, one of \"" + chars + "\" expected", file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
}
|
2010-06-15 13:37:41 +00:00
|
|
|
throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
|
2013-11-15 00:32:46 -08:00
|
|
|
return (c); // shouldn't reach here, but some compilers require it
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
// TODO: Should we check for all other official escapes here (and
|
|
|
|
// error on the rest)?
|
2010-03-15 08:35:45 +00:00
|
|
|
std::string
|
2013-01-08 20:12:37 -08:00
|
|
|
strFromStringstream(std::istream& in, const std::string& file,
|
2020-04-17 17:11:56 +03:00
|
|
|
const int line, int& pos) {
|
2009-10-30 17:28:17 +00:00
|
|
|
std::stringstream ss;
|
2012-12-21 10:11:05 +01:00
|
|
|
int c = in.get();
|
2010-03-15 08:24:50 +00:00
|
|
|
++pos;
|
2009-10-30 17:28:17 +00:00
|
|
|
if (c == '"') {
|
|
|
|
c = in.get();
|
2010-03-15 08:24:50 +00:00
|
|
|
++pos;
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2010-06-15 13:37:41 +00:00
|
|
|
throwJSONError("String expected", file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2012-03-29 17:10:45 +02:00
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
while (c != EOF && c != '"') {
|
2012-03-29 17:10:45 +02:00
|
|
|
if (c == '\\') {
|
|
|
|
// see the spec for allowed escape characters
|
2018-06-18 17:11:05 +02:00
|
|
|
int d;
|
2012-03-30 11:14:57 +02:00
|
|
|
switch (in.peek()) {
|
|
|
|
case '"':
|
|
|
|
c = '"';
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
c = '/';
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
c = '\\';
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
c = '\b';
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
c = '\f';
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
c = '\n';
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
c = '\r';
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
c = '\t';
|
|
|
|
break;
|
2018-06-18 17:11:05 +02:00
|
|
|
case 'u':
|
|
|
|
// skip first 0
|
|
|
|
in.ignore();
|
|
|
|
++pos;
|
|
|
|
c = in.peek();
|
|
|
|
if (c != '0') {
|
|
|
|
throwJSONError("Unsupported unicode escape", file, line, pos);
|
|
|
|
}
|
|
|
|
// skip second 0
|
|
|
|
in.ignore();
|
|
|
|
++pos;
|
|
|
|
c = in.peek();
|
|
|
|
if (c != '0') {
|
|
|
|
throwJSONError("Unsupported unicode escape", file, line, pos - 2);
|
|
|
|
}
|
|
|
|
// get first digit
|
|
|
|
in.ignore();
|
|
|
|
++pos;
|
|
|
|
d = in.peek();
|
|
|
|
if ((d >= '0') && (d <= '9')) {
|
|
|
|
c = (d - '0') << 4;
|
|
|
|
} else if ((d >= 'A') && (d <= 'F')) {
|
|
|
|
c = (d - 'A' + 10) << 4;
|
|
|
|
} else if ((d >= 'a') && (d <= 'f')) {
|
|
|
|
c = (d - 'a' + 10) << 4;
|
|
|
|
} else {
|
|
|
|
throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 3);
|
|
|
|
}
|
|
|
|
// get second digit
|
|
|
|
in.ignore();
|
|
|
|
++pos;
|
|
|
|
d = in.peek();
|
|
|
|
if ((d >= '0') && (d <= '9')) {
|
|
|
|
c |= d - '0';
|
|
|
|
} else if ((d >= 'A') && (d <= 'F')) {
|
|
|
|
c |= d - 'A' + 10;
|
|
|
|
} else if ((d >= 'a') && (d <= 'f')) {
|
|
|
|
c |= d - 'a' + 10;
|
|
|
|
} else {
|
|
|
|
throwJSONError("Not hexadecimal in unicode escape", file, line, pos - 4);
|
|
|
|
}
|
|
|
|
break;
|
2012-03-30 11:14:57 +02:00
|
|
|
default:
|
2012-03-29 17:56:30 +02:00
|
|
|
throwJSONError("Bad escape", file, line, pos);
|
2012-03-29 17:10:45 +02:00
|
|
|
}
|
2012-03-30 11:14:57 +02:00
|
|
|
// drop the escaped char
|
2013-02-09 14:52:37 -08:00
|
|
|
in.ignore();
|
2012-03-30 11:14:57 +02:00
|
|
|
++pos;
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2012-12-21 10:11:05 +01:00
|
|
|
ss.put(c);
|
2009-10-30 17:28:17 +00:00
|
|
|
c = in.get();
|
2010-03-15 08:24:50 +00:00
|
|
|
++pos;
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2012-04-02 10:50:26 +02:00
|
|
|
if (c == EOF) {
|
|
|
|
throwJSONError("Unterminated string", file, line, pos);
|
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ss.str());
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
std::string
|
2013-01-08 20:12:37 -08:00
|
|
|
wordFromStringstream(std::istream& in, int& pos) {
|
2009-10-30 17:28:17 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
while (isalpha(in.peek())) {
|
|
|
|
ss << (char) in.get();
|
|
|
|
}
|
2009-11-04 13:41:12 +00:00
|
|
|
pos += ss.str().size();
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ss.str());
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2013-01-08 20:12:37 -08:00
|
|
|
std::string
|
|
|
|
numberFromStringstream(std::istream& in, int& pos) {
|
2010-06-30 11:49:10 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
|
|
|
|
in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
|
|
|
|
ss << (char) in.get();
|
2010-01-13 13:14:31 +00:00
|
|
|
}
|
2010-06-30 11:49:10 +00:00
|
|
|
pos += ss.str().size();
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ss.str());
|
2010-01-13 13:14:31 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 13:37:41 +00:00
|
|
|
// Should we change from IntElement and DoubleElement to NumberElement
|
|
|
|
// that can also hold an e value? (and have specific getters if the
|
|
|
|
// value is larger than an int can handle)
|
2013-07-03 17:08:12 +09:00
|
|
|
//
|
2023-10-02 17:37:25 +03:00
|
|
|
// At the moment of writing, the only way that the code flow can reach the
|
|
|
|
// int128_t cast, and not throw, is by retrieving one of the few bigint
|
|
|
|
// statistics through kea-ctrl-agent. If kea-ctrl-agent ever gets removed, and
|
2023-10-04 12:13:17 +03:00
|
|
|
// its HTTP listener embedded in Kea, then the cast to int128_t can be removed as
|
2023-10-02 17:37:25 +03:00
|
|
|
// well, as there is no deserialization of bigints required, although the only
|
|
|
|
// benefit would be better performance for error cases, so it's arguable.
|
2010-03-15 08:35:45 +00:00
|
|
|
ElementPtr
|
2014-04-17 18:53:43 +02:00
|
|
|
fromStringstreamNumber(std::istream& in, const std::string& file,
|
2020-04-17 17:11:56 +03:00
|
|
|
const int line, int& pos) {
|
2014-04-18 11:04:50 +02:00
|
|
|
// Remember position where the value starts. It will be set in the
|
|
|
|
// Position structure of the Element to be created.
|
2014-04-17 18:53:43 +02:00
|
|
|
const uint32_t start_pos = pos;
|
2014-04-18 11:04:50 +02:00
|
|
|
// This will move the pos to the end of the value.
|
2014-04-17 18:53:43 +02:00
|
|
|
const std::string number = numberFromStringstream(in, pos);
|
2010-06-30 11:49:10 +00:00
|
|
|
|
2023-10-02 15:07:39 +03:00
|
|
|
// Is it a double?
|
2013-07-08 11:43:54 +09:00
|
|
|
if (number.find_first_of(".eE") < number.size()) {
|
|
|
|
try {
|
2014-04-17 18:53:43 +02:00
|
|
|
return (Element::create(boost::lexical_cast<double>(number),
|
2014-04-22 16:40:29 +02:00
|
|
|
Element::Position(file, line, start_pos)));
|
2023-10-02 15:07:39 +03:00
|
|
|
} catch (const boost::bad_lexical_cast& exception) {
|
|
|
|
throwJSONError("Number overflow while trying to cast '" + number +
|
|
|
|
"' to double: " + exception.what(),
|
2014-04-18 18:11:48 +02:00
|
|
|
file, line, start_pos);
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
2023-10-02 15:07:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Is it an integer?
|
|
|
|
try {
|
|
|
|
return (Element::create(boost::lexical_cast<int64_t>(number),
|
|
|
|
Element::Position(file, line, start_pos)));
|
|
|
|
} catch (const boost::bad_lexical_cast& exception64) {
|
|
|
|
// Is it a big integer?
|
2013-07-08 11:43:54 +09:00
|
|
|
try {
|
2023-10-02 15:07:39 +03:00
|
|
|
return (Element::create(int128_t(number),
|
2014-04-22 16:40:29 +02:00
|
|
|
Element::Position(file, line, start_pos)));
|
2023-10-02 15:07:39 +03:00
|
|
|
} catch (overflow_error const& exception128) {
|
|
|
|
throwJSONError("Number overflow while trying to cast '" + number +
|
|
|
|
"' to int64 and subsequently to int128: " +
|
|
|
|
exception64.what() + ", " + exception128.what(),
|
|
|
|
file, line, start_pos);
|
2010-06-30 11:49:10 +00:00
|
|
|
}
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
2014-04-17 18:53:43 +02:00
|
|
|
return (ElementPtr());
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
ElementPtr
|
2013-01-08 20:12:37 -08:00
|
|
|
fromStringstreamBool(std::istream& in, const std::string& file,
|
2020-04-17 17:11:56 +03:00
|
|
|
const int line, int& pos) {
|
2014-04-18 11:04:50 +02:00
|
|
|
// Remember position where the value starts. It will be set in the
|
|
|
|
// Position structure of the Element to be created.
|
2014-04-17 18:53:43 +02:00
|
|
|
const uint32_t start_pos = pos;
|
2014-04-18 11:04:50 +02:00
|
|
|
// This will move the pos to the end of the value.
|
2013-01-08 20:12:37 -08:00
|
|
|
const std::string word = wordFromStringstream(in, pos);
|
2014-04-17 18:53:43 +02:00
|
|
|
|
2016-12-12 14:24:36 +01:00
|
|
|
if (word == "true") {
|
2014-04-22 16:40:29 +02:00
|
|
|
return (Element::create(true, Element::Position(file, line,
|
|
|
|
start_pos)));
|
2016-12-12 14:24:36 +01:00
|
|
|
} else if (word == "false") {
|
2014-04-22 16:40:29 +02:00
|
|
|
return (Element::create(false, Element::Position(file, line,
|
|
|
|
start_pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2014-04-17 18:53:43 +02:00
|
|
|
throwJSONError(std::string("Bad boolean value: ") + word, file,
|
|
|
|
line, start_pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2014-04-17 18:53:43 +02:00
|
|
|
return (ElementPtr());
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
ElementPtr
|
2013-01-08 20:12:37 -08:00
|
|
|
fromStringstreamNull(std::istream& in, const std::string& file,
|
2020-04-17 17:11:56 +03:00
|
|
|
const int line, int& pos) {
|
2014-04-18 11:04:50 +02:00
|
|
|
// Remember position where the value starts. It will be set in the
|
|
|
|
// Position structure of the Element to be created.
|
2014-04-17 18:53:43 +02:00
|
|
|
const uint32_t start_pos = pos;
|
2014-04-18 11:04:50 +02:00
|
|
|
// This will move the pos to the end of the value.
|
2013-01-08 20:12:37 -08:00
|
|
|
const std::string word = wordFromStringstream(in, pos);
|
2016-12-12 14:24:36 +01:00
|
|
|
if (word == "null") {
|
2014-04-22 16:40:29 +02:00
|
|
|
return (Element::create(Element::Position(file, line, start_pos)));
|
2010-06-15 10:11:48 +00:00
|
|
|
} else {
|
2014-04-17 18:53:43 +02:00
|
|
|
throwJSONError(std::string("Bad null value: ") + word, file,
|
|
|
|
line, start_pos);
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ElementPtr());
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
ElementPtr
|
2013-01-08 20:12:37 -08:00
|
|
|
fromStringstreamString(std::istream& in, const std::string& file, int& line,
|
2020-04-17 17:11:56 +03:00
|
|
|
int& pos) {
|
2014-04-18 11:04:50 +02:00
|
|
|
// Remember position where the value starts. It will be set in the
|
|
|
|
// Position structure of the Element to be created.
|
2014-04-17 18:53:43 +02:00
|
|
|
const uint32_t start_pos = pos;
|
2014-04-18 11:04:50 +02:00
|
|
|
// This will move the pos to the end of the value.
|
2014-04-17 18:53:43 +02:00
|
|
|
const std::string string_value = strFromStringstream(in, file, line, pos);
|
2014-04-22 16:40:29 +02:00
|
|
|
return (Element::create(string_value, Element::Position(file, line,
|
|
|
|
start_pos)));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
ElementPtr
|
2013-01-08 20:12:37 -08:00
|
|
|
fromStringstreamList(std::istream& in, const std::string& file, int& line,
|
2020-04-17 17:11:56 +03:00
|
|
|
int& pos) {
|
2012-12-21 10:11:05 +01:00
|
|
|
int c = 0;
|
2014-04-22 16:40:29 +02:00
|
|
|
ElementPtr list = Element::createList(Element::Position(file, line, pos));
|
2016-11-24 14:44:00 +01:00
|
|
|
ElementPtr cur_list_element;
|
2009-10-30 17:28:17 +00:00
|
|
|
|
2013-01-08 20:12:37 -08:00
|
|
|
skipChars(in, WHITESPACE, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
while (c != EOF && c != ']') {
|
2009-11-04 13:41:12 +00:00
|
|
|
if (in.peek() != ']') {
|
2010-06-15 13:02:26 +00:00
|
|
|
cur_list_element = Element::fromJSON(in, file, line, pos);
|
2010-06-15 12:38:55 +00:00
|
|
|
list->add(cur_list_element);
|
2013-10-28 00:32:16 -07:00
|
|
|
c = skipTo(in, file, line, pos, ",]", WHITESPACE);
|
|
|
|
} else {
|
|
|
|
c = in.get();
|
|
|
|
++pos;
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (list);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-03-15 08:35:45 +00:00
|
|
|
ElementPtr
|
2013-01-08 20:12:37 -08:00
|
|
|
fromStringstreamMap(std::istream& in, const std::string& file, int& line,
|
2020-04-17 17:11:56 +03:00
|
|
|
int& pos) {
|
2014-04-22 16:40:29 +02:00
|
|
|
ElementPtr map = Element::createMap(Element::Position(file, line, pos));
|
2013-01-08 20:12:37 -08:00
|
|
|
skipChars(in, WHITESPACE, line, pos);
|
2012-12-21 10:11:05 +01:00
|
|
|
int c = in.peek();
|
2011-06-29 10:21:47 +02:00
|
|
|
if (c == EOF) {
|
|
|
|
throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
|
2011-07-01 15:55:37 +02:00
|
|
|
} else if (c == '}') {
|
2010-02-03 11:38:38 +00:00
|
|
|
// empty map, skip closing curly
|
2013-02-09 14:52:37 -08:00
|
|
|
in.ignore();
|
2010-02-03 11:38:38 +00:00
|
|
|
} else {
|
|
|
|
while (c != EOF && c != '}') {
|
2013-01-08 20:12:37 -08:00
|
|
|
std::string key = strFromStringstream(in, file, line, pos);
|
2010-03-31 23:55:26 +00:00
|
|
|
|
2013-01-08 20:12:37 -08:00
|
|
|
skipTo(in, file, line, pos, ":", WHITESPACE);
|
2010-02-03 11:38:38 +00:00
|
|
|
// skip the :
|
2010-06-15 12:38:55 +00:00
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr value = Element::fromJSON(in, file, line, pos);
|
2010-06-15 12:38:55 +00:00
|
|
|
map->set(key, value);
|
2012-09-27 16:00:48 +02:00
|
|
|
|
2013-10-28 00:32:16 -07:00
|
|
|
c = skipTo(in, file, line, pos, ",}", WHITESPACE);
|
2010-02-03 11:38:38 +00:00
|
|
|
}
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (map);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2020-04-17 14:46:42 +03:00
|
|
|
} // end anonymous namespace
|
2009-10-30 17:28:17 +00:00
|
|
|
|
2010-06-27 19:37:00 +00:00
|
|
|
std::string
|
2010-10-15 08:59:24 +00:00
|
|
|
Element::typeToName(Element::types type) {
|
2010-08-16 22:08:14 +00:00
|
|
|
switch (type) {
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::integer:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("integer"));
|
2023-04-26 15:44:33 +03:00
|
|
|
case Element::bigint:
|
|
|
|
return (std::string("bigint"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::real:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("real"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::boolean:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("boolean"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::string:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("string"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::list:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("list"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::map:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("map"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::null:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("null"));
|
2010-06-27 19:37:00 +00:00
|
|
|
case Element::any:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("any"));
|
2010-06-27 19:37:00 +00:00
|
|
|
default:
|
2010-08-16 22:08:14 +00:00
|
|
|
return (std::string("unknown"));
|
2010-06-27 19:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Element::types
|
|
|
|
Element::nameToType(const std::string& type_name) {
|
|
|
|
if (type_name == "integer") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::integer);
|
2023-04-26 15:44:33 +03:00
|
|
|
} else if (type_name == "bigint") {
|
|
|
|
return (Element::bigint);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "real") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::real);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "boolean") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::boolean);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "string") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::string);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "list") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::list);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "map") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::map);
|
2011-07-15 11:33:27 +02:00
|
|
|
} else if (type_name == "named_set") {
|
2011-07-08 09:41:27 +02:00
|
|
|
return (Element::map);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "null") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::null);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else if (type_name == "any") {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (Element::any);
|
2010-06-27 19:37:00 +00:00
|
|
|
} else {
|
|
|
|
isc_throw(TypeError, type_name + " is not a valid type name");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
ElementPtr
|
2017-08-18 10:27:29 +03:00
|
|
|
Element::fromJSON(std::istream& in, bool preproc) {
|
2014-05-08 16:01:07 +02:00
|
|
|
|
2009-11-04 13:41:12 +00:00
|
|
|
int line = 1, pos = 1;
|
2014-05-19 14:15:29 +02:00
|
|
|
stringstream filtered;
|
|
|
|
if (preproc) {
|
|
|
|
preprocess(in, filtered);
|
|
|
|
}
|
|
|
|
|
2014-05-19 15:21:17 +02:00
|
|
|
ElementPtr value = fromJSON(preproc ? filtered : in, "<istream>", line, pos);
|
2014-05-19 14:15:29 +02:00
|
|
|
|
|
|
|
return (value);
|
2010-02-03 11:38:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2020-04-17 17:11:56 +03:00
|
|
|
Element::fromJSON(std::istream& in, const std::string& file_name, bool preproc) {
|
2010-02-03 11:38:38 +00:00
|
|
|
int line = 1, pos = 1;
|
2014-05-19 14:15:29 +02:00
|
|
|
stringstream filtered;
|
|
|
|
if (preproc) {
|
|
|
|
preprocess(in, filtered);
|
|
|
|
}
|
2014-05-19 15:21:17 +02:00
|
|
|
return (fromJSON(preproc ? filtered : in, file_name, line, pos));
|
2009-11-04 13:41:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2013-01-08 20:12:37 -08:00
|
|
|
Element::fromJSON(std::istream& in, const std::string& file, int& line,
|
2020-04-17 17:11:56 +03:00
|
|
|
int& pos) {
|
2012-12-21 10:11:05 +01:00
|
|
|
int c = 0;
|
2009-10-30 17:28:17 +00:00
|
|
|
ElementPtr element;
|
|
|
|
bool el_read = false;
|
2013-01-08 20:12:37 -08:00
|
|
|
skipChars(in, WHITESPACE, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
while (c != EOF && !el_read) {
|
|
|
|
c = in.get();
|
2009-11-04 13:41:12 +00:00
|
|
|
pos++;
|
2009-10-30 17:28:17 +00:00
|
|
|
switch(c) {
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
|
|
|
case '0':
|
2010-06-27 19:37:00 +00:00
|
|
|
case '-':
|
|
|
|
case '+':
|
2010-06-30 11:49:10 +00:00
|
|
|
case '.':
|
|
|
|
in.putback(c);
|
2013-10-28 00:32:16 -07:00
|
|
|
--pos;
|
2014-04-17 18:53:43 +02:00
|
|
|
element = fromStringstreamNumber(in, file, line, pos);
|
2010-06-27 19:37:00 +00:00
|
|
|
el_read = true;
|
|
|
|
break;
|
2009-10-30 17:28:17 +00:00
|
|
|
case 't':
|
|
|
|
case 'f':
|
|
|
|
in.putback(c);
|
2013-10-28 00:32:16 -07:00
|
|
|
--pos;
|
2013-01-08 20:12:37 -08:00
|
|
|
element = fromStringstreamBool(in, file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
el_read = true;
|
|
|
|
break;
|
2010-06-15 10:11:48 +00:00
|
|
|
case 'n':
|
|
|
|
in.putback(c);
|
2013-10-28 00:32:16 -07:00
|
|
|
--pos;
|
2013-01-08 20:12:37 -08:00
|
|
|
element = fromStringstreamNull(in, file, line, pos);
|
2010-06-15 10:11:48 +00:00
|
|
|
el_read = true;
|
|
|
|
break;
|
2009-10-30 17:28:17 +00:00
|
|
|
case '"':
|
|
|
|
in.putback('"');
|
2013-10-28 00:32:16 -07:00
|
|
|
--pos;
|
2013-01-08 20:12:37 -08:00
|
|
|
element = fromStringstreamString(in, file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
el_read = true;
|
|
|
|
break;
|
|
|
|
case '[':
|
2013-01-08 20:12:37 -08:00
|
|
|
element = fromStringstreamList(in, file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
el_read = true;
|
|
|
|
break;
|
|
|
|
case '{':
|
2013-01-08 20:12:37 -08:00
|
|
|
element = fromStringstreamMap(in, file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
el_read = true;
|
|
|
|
break;
|
2009-11-04 13:41:12 +00:00
|
|
|
case EOF:
|
|
|
|
break;
|
2009-10-30 17:28:17 +00:00
|
|
|
default:
|
2012-12-21 10:11:05 +01:00
|
|
|
throwJSONError(std::string("error: unexpected character ") + std::string(1, c), file, line, pos);
|
2009-10-30 17:28:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (el_read) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (element);
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2010-06-15 14:40:31 +00:00
|
|
|
isc_throw(JSONError, "nothing read");
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-03 13:07:00 +00:00
|
|
|
ElementPtr
|
2014-05-08 16:01:07 +02:00
|
|
|
Element::fromJSON(const std::string& in, bool preproc) {
|
2009-11-03 13:07:00 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
ss << in;
|
2014-05-08 16:01:07 +02:00
|
|
|
|
2012-04-02 15:16:27 +02:00
|
|
|
int line = 1, pos = 1;
|
2014-05-19 14:15:29 +02:00
|
|
|
stringstream filtered;
|
|
|
|
if (preproc) {
|
|
|
|
preprocess(ss, filtered);
|
|
|
|
}
|
2014-05-19 15:21:17 +02:00
|
|
|
ElementPtr result(fromJSON(preproc ? filtered : ss, "<string>", line, pos));
|
2013-01-08 20:12:37 -08:00
|
|
|
skipChars(ss, WHITESPACE, line, pos);
|
2012-04-02 15:16:27 +02:00
|
|
|
// ss must now be at end
|
|
|
|
if (ss.peek() != EOF) {
|
|
|
|
throwJSONError("Extra data", "<string>", line, pos);
|
|
|
|
}
|
|
|
|
return result;
|
2009-11-03 13:07:00 +00:00
|
|
|
}
|
2010-01-13 13:14:31 +00:00
|
|
|
|
2014-05-08 17:25:26 +02:00
|
|
|
ElementPtr
|
2020-04-17 17:11:56 +03:00
|
|
|
Element::fromJSONFile(const std::string& file_name, bool preproc) {
|
2015-09-21 17:21:30 -07:00
|
|
|
// zero out the errno to be safe
|
|
|
|
errno = 0;
|
|
|
|
|
2014-05-08 17:25:26 +02:00
|
|
|
std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
|
2020-04-17 17:11:56 +03:00
|
|
|
if (!infile.is_open()) {
|
2014-05-19 15:21:17 +02:00
|
|
|
const char* error = strerror(errno);
|
2014-08-25 20:10:39 +02:00
|
|
|
isc_throw(InvalidOperation, "failed to read file '" << file_name
|
|
|
|
<< "': " << error);
|
2014-05-08 17:25:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (fromJSON(infile, file_name, preproc));
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
// to JSON format
|
|
|
|
|
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
IntElement::toJSON(std::ostream& ss) const {
|
2009-12-24 11:30:19 +00:00
|
|
|
ss << intValue();
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2023-04-26 15:44:33 +03:00
|
|
|
void
|
|
|
|
BigIntElement::toJSON(std::ostream& ss) const {
|
|
|
|
ss << bigIntValue();
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
DoubleElement::toJSON(std::ostream& ss) const {
|
2019-01-09 11:14:01 -05:00
|
|
|
// The default output for doubles nicely drops off trailing
|
|
|
|
// zeros, however this produces strings without decimal points
|
|
|
|
// for whole number values. When reparsed this will create
|
|
|
|
// IntElements not DoubleElements. Rather than used a fixed
|
|
|
|
// precision, we'll just tack on an ".0" when the decimal point
|
|
|
|
// is missing.
|
|
|
|
ostringstream val_ss;
|
|
|
|
val_ss << doubleValue();
|
|
|
|
ss << val_ss.str();
|
|
|
|
if (val_ss.str().find_first_of('.') == string::npos) {
|
|
|
|
ss << ".0";
|
|
|
|
}
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
BoolElement::toJSON(std::ostream& ss) const {
|
2010-06-27 19:37:00 +00:00
|
|
|
if (boolValue()) {
|
2010-06-15 10:11:48 +00:00
|
|
|
ss << "true";
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2010-06-15 10:11:48 +00:00
|
|
|
ss << "false";
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
NullElement::toJSON(std::ostream& ss) const {
|
2010-06-15 10:11:48 +00:00
|
|
|
ss << "null";
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
StringElement::toJSON(std::ostream& ss) const {
|
2009-10-30 17:28:17 +00:00
|
|
|
ss << "\"";
|
2012-03-29 17:10:45 +02:00
|
|
|
const std::string& str = stringValue();
|
|
|
|
for (size_t i = 0; i < str.size(); ++i) {
|
2013-06-13 15:40:14 +05:30
|
|
|
const char c = str[i];
|
2012-03-29 17:10:45 +02:00
|
|
|
// Escape characters as defined in JSON spec
|
2012-03-29 17:56:30 +02:00
|
|
|
// Note that we do not escape forward slash; this
|
|
|
|
// is allowed, but not mandatory.
|
2012-03-30 11:14:57 +02:00
|
|
|
switch (c) {
|
|
|
|
case '"':
|
|
|
|
ss << '\\' << c;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
ss << '\\' << c;
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
ss << '\\' << 'b';
|
|
|
|
break;
|
|
|
|
case '\f':
|
|
|
|
ss << '\\' << 'f';
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
ss << '\\' << 'n';
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
ss << '\\' << 'r';
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
ss << '\\' << 't';
|
|
|
|
break;
|
|
|
|
default:
|
2018-06-18 17:11:05 +02:00
|
|
|
if (((c >= 0) && (c < 0x20)) || (c < 0) || (c >= 0x7f)) {
|
2017-01-04 15:33:03 +01:00
|
|
|
std::ostringstream esc;
|
|
|
|
esc << "\\u"
|
|
|
|
<< hex
|
|
|
|
<< setw(4)
|
|
|
|
<< setfill('0')
|
|
|
|
<< (static_cast<unsigned>(c) & 0xff);
|
|
|
|
ss << esc.str();
|
2016-12-11 18:47:58 +01:00
|
|
|
} else {
|
|
|
|
ss << c;
|
|
|
|
}
|
2012-03-29 17:10:45 +02:00
|
|
|
}
|
|
|
|
}
|
2009-10-30 17:28:17 +00:00
|
|
|
ss << "\"";
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
ListElement::toJSON(std::ostream& ss) const {
|
2009-10-30 17:28:17 +00:00
|
|
|
ss << "[ ";
|
2010-03-15 08:24:50 +00:00
|
|
|
|
2016-11-24 14:44:00 +01:00
|
|
|
const std::vector<ElementPtr>& v = listValue();
|
2020-04-17 15:22:11 +03:00
|
|
|
for (auto it = v.begin(); it != v.end(); ++it) {
|
2009-10-30 17:28:17 +00:00
|
|
|
if (it != v.begin()) {
|
|
|
|
ss << ", ";
|
|
|
|
}
|
2010-06-15 10:11:48 +00:00
|
|
|
(*it)->toJSON(ss);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
ss << " ]";
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
MapElement::toJSON(std::ostream& ss) const {
|
2010-06-15 10:11:48 +00:00
|
|
|
ss << "{ ";
|
2010-03-15 08:24:50 +00:00
|
|
|
|
2020-04-17 15:22:11 +03:00
|
|
|
for (auto it = m.begin(); it != m.end(); ++it) {
|
2009-10-30 17:28:17 +00:00
|
|
|
if (it != m.begin()) {
|
|
|
|
ss << ", ";
|
|
|
|
}
|
|
|
|
ss << "\"" << (*it).first << "\": ";
|
2010-02-22 12:42:15 +00:00
|
|
|
if ((*it).second) {
|
2010-06-15 10:11:48 +00:00
|
|
|
(*it).second->toJSON(ss);
|
2010-02-22 12:42:15 +00:00
|
|
|
} else {
|
|
|
|
ss << "None";
|
|
|
|
}
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2010-06-15 10:11:48 +00:00
|
|
|
ss << " }";
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-01-04 12:59:22 +00:00
|
|
|
// throws when one of the types in the path (except the one
|
2009-10-30 17:28:17 +00:00
|
|
|
// we're looking for) is not a MapElement
|
|
|
|
// returns 0 if it could simply not be found
|
|
|
|
// should that also be an exception?
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr
|
|
|
|
MapElement::find(const std::string& id) const {
|
2010-03-15 08:24:50 +00:00
|
|
|
const size_t sep = id.find('/');
|
2009-10-30 17:28:17 +00:00
|
|
|
if (sep == std::string::npos) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (get(id));
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr ce = get(id.substr(0, sep));
|
2009-10-30 17:28:17 +00:00
|
|
|
if (ce) {
|
2009-12-18 17:41:23 +00:00
|
|
|
// ignore trailing slash
|
2010-03-15 08:24:50 +00:00
|
|
|
if (sep + 1 != id.size()) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ce->find(id.substr(sep + 1)));
|
2009-12-18 17:41:23 +00:00
|
|
|
} else {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ce);
|
2009-12-18 17:41:23 +00:00
|
|
|
}
|
2009-10-30 17:28:17 +00:00
|
|
|
} else {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (ElementPtr());
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2010-03-15 08:24:50 +00:00
|
|
|
Element::fromWire(const std::string& s) {
|
2009-10-30 17:28:17 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
ss << s;
|
2010-06-15 10:11:48 +00:00
|
|
|
int line = 0, pos = 0;
|
2010-08-16 22:08:14 +00:00
|
|
|
return (fromJSON(ss, "<wire>", line, pos));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ElementPtr
|
2010-11-08 16:01:31 +00:00
|
|
|
Element::fromWire(std::stringstream& in, int) {
|
2009-10-30 17:28:17 +00:00
|
|
|
//
|
|
|
|
// Check protocol version
|
|
|
|
//
|
2010-06-15 10:11:48 +00:00
|
|
|
//for (int i = 0 ; i < 4 ; ++i) {
|
|
|
|
// const unsigned char version_byte = get_byte(in);
|
|
|
|
// if (PROTOCOL_VERSION[i] != version_byte) {
|
|
|
|
// throw DecodeError("Protocol version incorrect");
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//length -= 4;
|
|
|
|
int line = 0, pos = 0;
|
2010-08-16 22:08:14 +00:00
|
|
|
return (fromJSON(in, "<wire>", line, pos));
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 12:38:55 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
MapElement::set(const std::string& key, ConstElementPtr value) {
|
2010-06-27 19:37:00 +00:00
|
|
|
m[key] = value;
|
2010-06-15 12:38:55 +00:00
|
|
|
}
|
|
|
|
|
2009-10-30 17:28:17 +00:00
|
|
|
bool
|
2012-10-10 03:51:09 +05:30
|
|
|
MapElement::find(const std::string& id, ConstElementPtr& t) const {
|
2009-10-30 17:28:17 +00:00
|
|
|
try {
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr p = find(id);
|
2009-10-30 17:28:17 +00:00
|
|
|
if (p) {
|
|
|
|
t = p;
|
2010-08-16 22:08:14 +00:00
|
|
|
return (true);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2011-12-12 17:34:26 +00:00
|
|
|
} catch (const TypeError&) {
|
2009-10-30 17:28:17 +00:00
|
|
|
// ignore
|
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2009-10-30 17:28:17 +00:00
|
|
|
}
|
2009-12-18 17:41:23 +00:00
|
|
|
|
2010-02-28 20:00:49 +00:00
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
IntElement::equals(const Element& other) const {
|
2023-04-26 15:44:33 +03:00
|
|
|
// Let's not be very picky with constraining the integer types to be the
|
|
|
|
// same. Equality is sometimes checked from high-up in the Element hierarcy.
|
|
|
|
// That is a context which, most of the time, does not have information on
|
|
|
|
// the type of integers stored on Elements lower in the hierarchy. So it
|
|
|
|
// would be difficult to differentiate between the integer types.
|
|
|
|
return (other.getType() == Element::integer && i == other.intValue()) ||
|
|
|
|
(other.getType() == Element::bigint && i == other.bigIntValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
BigIntElement::equals(const Element& other) const {
|
|
|
|
// Let's not be very picky with constraining the integer types to be the
|
|
|
|
// same. Equality is sometimes checked from high-up in the Element hierarcy.
|
|
|
|
// That is a context which, most of the time, does not have information on
|
|
|
|
// the type of integers stored on Elements lower in the hierarchy. So it
|
|
|
|
// would be difficult to differentiate between the integer types.
|
|
|
|
return (other.getType() == Element::bigint && i_ == other.bigIntValue()) ||
|
|
|
|
(other.getType() == Element::integer && i_ == other.intValue());
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
DoubleElement::equals(const Element& other) const {
|
|
|
|
return (other.getType() == Element::real) &&
|
2019-08-13 14:35:45 +02:00
|
|
|
(fabs(d - other.doubleValue()) < 1e-14);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
BoolElement::equals(const Element& other) const {
|
|
|
|
return (other.getType() == Element::boolean) &&
|
|
|
|
(b == other.boolValue());
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 10:11:48 +00:00
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
NullElement::equals(const Element& other) const {
|
|
|
|
return (other.getType() == Element::null);
|
2010-06-15 10:11:48 +00:00
|
|
|
}
|
|
|
|
|
2010-02-28 20:00:49 +00:00
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
StringElement::equals(const Element& other) const {
|
|
|
|
return (other.getType() == Element::string) &&
|
|
|
|
(s == other.stringValue());
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
ListElement::equals(const Element& other) const {
|
|
|
|
if (other.getType() == Element::list) {
|
2011-12-12 17:09:00 +00:00
|
|
|
const size_t s = size();
|
2010-08-16 23:36:54 +00:00
|
|
|
if (s != other.size()) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
2011-12-12 17:09:00 +00:00
|
|
|
for (size_t i = 0; i < s; ++i) {
|
2010-08-16 23:36:54 +00:00
|
|
|
if (!get(i)->equals(*other.get(i))) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (true);
|
2010-02-28 20:00:49 +00:00
|
|
|
} else {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-15 15:14:20 +03:00
|
|
|
void
|
|
|
|
ListElement::sort(std::string const& index /* = std::string() */) {
|
|
|
|
if (l.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int const t(l.at(0)->getType());
|
|
|
|
std::function<bool(ElementPtr, ElementPtr)> comparator;
|
|
|
|
if (t == map) {
|
|
|
|
if (index.empty()) {
|
|
|
|
isc_throw(BadValue, "index required when sorting maps");
|
|
|
|
}
|
|
|
|
comparator = [&](ElementPtr const& a, ElementPtr const& b) {
|
|
|
|
ConstElementPtr const& ai(a->get(index));
|
|
|
|
ConstElementPtr const& bi(b->get(index));
|
|
|
|
if (ai && bi) {
|
|
|
|
return *ai < *bi;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
} else if (t == list) {
|
|
|
|
// Nested lists. Not supported.
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// Assume scalars.
|
|
|
|
if (!index.empty()) {
|
|
|
|
isc_throw(BadValue, "index given when sorting scalars?");
|
|
|
|
}
|
|
|
|
comparator = [&](ElementPtr const& a, ElementPtr const& b) {
|
|
|
|
return *a < *b;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::sort(l.begin(), l.end(), comparator);
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:00:49 +00:00
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
MapElement::equals(const Element& other) const {
|
|
|
|
if (other.getType() == Element::map) {
|
2020-04-17 12:32:39 +03:00
|
|
|
if (size() != other.size()) {
|
|
|
|
return (false);
|
|
|
|
}
|
2020-04-17 15:00:05 +02:00
|
|
|
for (auto kv : mapValue()) {
|
|
|
|
auto key = kv.first;
|
|
|
|
if (other.contains(key)) {
|
|
|
|
if (!get(key)->equals(*other.get(key))) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 22:08:14 +00:00
|
|
|
return (true);
|
2010-02-28 20:00:49 +00:00
|
|
|
} else {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (false);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-18 17:41:23 +00:00
|
|
|
bool
|
2010-08-16 23:36:54 +00:00
|
|
|
isNull(ConstElementPtr p) {
|
2010-08-16 22:08:14 +00:00
|
|
|
return (!p);
|
2009-12-18 17:41:23 +00:00
|
|
|
}
|
|
|
|
|
2010-02-28 20:00:49 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
removeIdentical(ElementPtr a, ConstElementPtr b) {
|
2010-02-28 20:33:42 +00:00
|
|
|
if (!b) {
|
|
|
|
return;
|
|
|
|
}
|
2010-02-28 20:00:49 +00:00
|
|
|
if (a->getType() != Element::map || b->getType() != Element::map) {
|
2010-03-07 07:06:42 +00:00
|
|
|
isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
2010-02-28 20:33:42 +00:00
|
|
|
|
2011-12-12 18:12:39 +00:00
|
|
|
// As maps do not allow entries with multiple keys, we can either iterate
|
|
|
|
// over a checking for identical entries in b or vice-versa. As elements
|
|
|
|
// are removed from a if a match is found, we choose to iterate over b to
|
|
|
|
// avoid problems with element removal affecting the iterator.
|
2020-04-17 15:00:05 +02:00
|
|
|
for (auto kv : b->mapValue()) {
|
|
|
|
auto key = kv.first;
|
|
|
|
if (a->contains(key)) {
|
|
|
|
if (a->get(key)->equals(*b->get(key))) {
|
|
|
|
a->remove(key);
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-02-28 20:33:42 +00:00
|
|
|
}
|
2010-02-28 20:00:49 +00:00
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
ConstElementPtr
|
|
|
|
removeIdentical(ConstElementPtr a, ConstElementPtr b) {
|
|
|
|
ElementPtr result = Element::createMap();
|
|
|
|
|
|
|
|
if (!b) {
|
|
|
|
return (result);
|
|
|
|
}
|
2012-09-27 16:00:48 +02:00
|
|
|
|
2010-08-16 23:36:54 +00:00
|
|
|
if (a->getType() != Element::map || b->getType() != Element::map) {
|
|
|
|
isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
|
|
|
|
}
|
|
|
|
|
2023-12-04 10:18:55 +01:00
|
|
|
for (const auto& kv : a->mapValue()) {
|
2020-04-17 15:00:05 +02:00
|
|
|
auto key = kv.first;
|
|
|
|
if (!b->contains(key) ||
|
|
|
|
!a->get(key)->equals(*b->get(key))) {
|
|
|
|
result->set(key, kv.second);
|
2010-08-16 23:36:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:33:42 +00:00
|
|
|
void
|
2010-08-16 23:36:54 +00:00
|
|
|
merge(ElementPtr element, ConstElementPtr other) {
|
2010-02-28 20:33:42 +00:00
|
|
|
if (element->getType() != Element::map ||
|
|
|
|
other->getType() != Element::map) {
|
2010-03-07 07:06:42 +00:00
|
|
|
isc_throw(TypeError, "merge arguments not MapElements");
|
2010-02-28 20:33:42 +00:00
|
|
|
}
|
2012-09-27 16:00:48 +02:00
|
|
|
|
2023-12-04 10:18:55 +01:00
|
|
|
for (const auto& kv : other->mapValue()) {
|
2020-04-17 15:00:05 +02:00
|
|
|
auto key = kv.first;
|
|
|
|
auto value = kv.second;
|
|
|
|
if (value && value->getType() != Element::null) {
|
|
|
|
element->set(key, value);
|
|
|
|
} else if (element->contains(key)) {
|
|
|
|
element->remove(key);
|
2010-02-28 20:33:42 +00:00
|
|
|
}
|
|
|
|
}
|
2010-02-28 20:00:49 +00:00
|
|
|
}
|
2010-02-28 20:33:42 +00:00
|
|
|
|
2022-06-08 19:11:43 +03:00
|
|
|
void
|
|
|
|
mergeDiffAdd(ElementPtr& element, ElementPtr& other,
|
|
|
|
HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
|
|
|
|
if (element->getType() != other->getType()) {
|
|
|
|
isc_throw(TypeError, "mergeDiffAdd arguments not same type");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element->getType() == Element::list) {
|
|
|
|
// Store new elements in a separate container so we don't overwrite
|
|
|
|
// options as we add them (if there are duplicates).
|
|
|
|
ElementPtr new_elements = Element::createList();
|
|
|
|
for (auto& right : other->listValue()) {
|
|
|
|
// Check if we have any description of the key in the configuration
|
|
|
|
// hierarchy.
|
|
|
|
auto f = hierarchy[idx].find(key);
|
|
|
|
if (f != hierarchy[idx].end()) {
|
|
|
|
bool found = false;
|
2022-06-23 13:46:44 +03:00
|
|
|
ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
|
2022-06-08 19:11:43 +03:00
|
|
|
for (auto& left : element->listValue()) {
|
|
|
|
ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
|
|
|
|
// Check if the elements refer to the same configuration
|
|
|
|
// entity.
|
|
|
|
if (f->second.match_(mutable_left, mutable_right)) {
|
|
|
|
found = true;
|
|
|
|
mergeDiffAdd(mutable_left, mutable_right, hierarchy, key, idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
new_elements->add(right);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
new_elements->add(right);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Finally add the new elements.
|
|
|
|
for (auto& right : new_elements->listValue()) {
|
|
|
|
element->add(right);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element->getType() == Element::map) {
|
|
|
|
for (auto kv : other->mapValue()) {
|
|
|
|
auto current_key = kv.first;
|
|
|
|
auto value = boost::const_pointer_cast<Element>(kv.second);
|
|
|
|
if (value && value->getType() != Element::null) {
|
|
|
|
if (element->contains(current_key) &&
|
|
|
|
(value->getType() == Element::map ||
|
|
|
|
value->getType() == Element::list)) {
|
|
|
|
ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
|
|
|
|
mergeDiffAdd(mutable_element, value, hierarchy, current_key, idx + 1);
|
|
|
|
} else {
|
|
|
|
element->set(current_key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
element = other;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mergeDiffDel(ElementPtr& element, ElementPtr& other,
|
|
|
|
HierarchyDescriptor& hierarchy, std::string key, size_t idx) {
|
|
|
|
if (element->getType() != other->getType()) {
|
|
|
|
isc_throw(TypeError, "mergeDiffDel arguments not same type");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element->getType() == Element::list) {
|
|
|
|
for (auto const& value : other->listValue()) {
|
2022-06-23 19:15:05 +03:00
|
|
|
ElementPtr mutable_right = boost::const_pointer_cast<Element>(value);
|
2022-06-08 19:11:43 +03:00
|
|
|
for (uint32_t iter = 0; iter < element->listValue().size();) {
|
|
|
|
bool removed = false;
|
|
|
|
// Check if we have any description of the key in the
|
|
|
|
// configuration hierarchy.
|
|
|
|
auto f = hierarchy[idx].find(key);
|
|
|
|
if (f != hierarchy[idx].end()) {
|
|
|
|
ElementPtr mutable_left = boost::const_pointer_cast<Element>(element->listValue().at(iter));
|
|
|
|
// Check if the elements refer to the same configuration
|
|
|
|
// entity.
|
|
|
|
if (f->second.match_(mutable_left, mutable_right)) {
|
|
|
|
// Check if the user supplied data only contains
|
|
|
|
// identification information, so the intent is to
|
|
|
|
// delete the element, not just element data.
|
|
|
|
if (f->second.no_data_(mutable_right)) {
|
|
|
|
element->remove(iter);
|
|
|
|
removed = true;
|
|
|
|
} else {
|
|
|
|
mergeDiffDel(mutable_left, mutable_right, hierarchy, key, idx);
|
|
|
|
if (mutable_left->empty()) {
|
|
|
|
element->remove(iter);
|
|
|
|
removed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (element->listValue().at(iter)->equals(*value)) {
|
|
|
|
element->remove(iter);
|
|
|
|
removed = true;
|
|
|
|
}
|
|
|
|
if (!removed) {
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element->getType() == Element::map) {
|
|
|
|
// If the resulting element still contains data, we need to restore the
|
|
|
|
// key parameters, so we store them here.
|
|
|
|
ElementPtr new_elements = Element::createMap();
|
|
|
|
for (auto kv : other->mapValue()) {
|
|
|
|
auto current_key = kv.first;
|
|
|
|
auto value = boost::const_pointer_cast<Element>(kv.second);
|
|
|
|
if (value && value->getType() != Element::null) {
|
|
|
|
if (element->contains(current_key)) {
|
|
|
|
ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
|
|
|
|
if (mutable_element->getType() == Element::map ||
|
|
|
|
mutable_element->getType() == Element::list) {
|
|
|
|
mergeDiffDel(mutable_element, value, hierarchy, current_key, idx + 1);
|
|
|
|
if (mutable_element->empty()) {
|
|
|
|
element->remove(current_key);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Check if we have any description of the key in the
|
|
|
|
// configuration hierarchy.
|
|
|
|
auto f = hierarchy[idx].find(key);
|
|
|
|
if (f != hierarchy[idx].end()) {
|
|
|
|
// Check if the key is used for element
|
|
|
|
// identification.
|
|
|
|
if (f->second.is_key_(current_key)) {
|
|
|
|
// Store the key parameter.
|
|
|
|
new_elements->set(current_key, mutable_element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
element->remove(current_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If the element still contains data, restore the key elements.
|
|
|
|
if (element->size()) {
|
|
|
|
for (auto kv : new_elements->mapValue()) {
|
|
|
|
element->set(kv.first, kv.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
element = ElementPtr(new NullElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
extend(const std::string& container, const std::string& extension,
|
|
|
|
ElementPtr& element, ElementPtr& other, HierarchyDescriptor& hierarchy,
|
|
|
|
std::string key, size_t idx, bool alter) {
|
|
|
|
if (element->getType() != other->getType()) {
|
|
|
|
isc_throw(TypeError, "extend arguments not same type");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element->getType() == Element::list) {
|
|
|
|
for (auto& right : other->listValue()) {
|
|
|
|
// Check if we have any description of the key in the configuration
|
|
|
|
// hierarchy.
|
|
|
|
auto f = hierarchy[idx].find(key);
|
|
|
|
if (f != hierarchy[idx].end()) {
|
2022-06-23 13:46:44 +03:00
|
|
|
ElementPtr mutable_right = boost::const_pointer_cast<Element>(right);
|
2022-06-08 19:11:43 +03:00
|
|
|
for (auto& left : element->listValue()) {
|
|
|
|
ElementPtr mutable_left = boost::const_pointer_cast<Element>(left);
|
|
|
|
if (container == key) {
|
|
|
|
alter = true;
|
|
|
|
}
|
|
|
|
if (f->second.match_(mutable_left, mutable_right)) {
|
|
|
|
extend(container, extension, mutable_left, mutable_right,
|
|
|
|
hierarchy, key, idx, alter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element->getType() == Element::map) {
|
|
|
|
for (auto kv : other->mapValue()) {
|
|
|
|
auto current_key = kv.first;
|
|
|
|
auto value = boost::const_pointer_cast<Element>(kv.second);
|
|
|
|
if (value && value->getType() != Element::null) {
|
|
|
|
if (element->contains(current_key) &&
|
|
|
|
(value->getType() == Element::map ||
|
|
|
|
value->getType() == Element::list)) {
|
|
|
|
ElementPtr mutable_element = boost::const_pointer_cast<Element>(element->get(current_key));
|
2022-06-24 19:12:56 +03:00
|
|
|
if (container == key) {
|
|
|
|
alter = true;
|
|
|
|
}
|
|
|
|
extend(container, extension, mutable_element, value, hierarchy, current_key, idx + 1, alter);
|
2022-06-08 19:11:43 +03:00
|
|
|
} else if (alter && current_key == extension) {
|
|
|
|
element->set(current_key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 09:03:09 +01:00
|
|
|
ElementPtr
|
|
|
|
copy(ConstElementPtr from, int level) {
|
2020-04-17 15:00:05 +02:00
|
|
|
if (!from) {
|
2017-03-05 09:03:09 +01:00
|
|
|
isc_throw(BadValue, "copy got a null pointer");
|
|
|
|
}
|
|
|
|
int from_type = from->getType();
|
|
|
|
if (from_type == Element::integer) {
|
|
|
|
return (ElementPtr(new IntElement(from->intValue())));
|
|
|
|
} else if (from_type == Element::real) {
|
|
|
|
return (ElementPtr(new DoubleElement(from->doubleValue())));
|
|
|
|
} else if (from_type == Element::boolean) {
|
|
|
|
return (ElementPtr(new BoolElement(from->boolValue())));
|
|
|
|
} else if (from_type == Element::null) {
|
|
|
|
return (ElementPtr(new NullElement()));
|
|
|
|
} else if (from_type == Element::string) {
|
|
|
|
return (ElementPtr(new StringElement(from->stringValue())));
|
|
|
|
} else if (from_type == Element::list) {
|
|
|
|
ElementPtr result = ElementPtr(new ListElement());
|
2020-04-17 15:00:05 +02:00
|
|
|
for (auto elem : from->listValue()) {
|
2017-03-05 09:03:09 +01:00
|
|
|
if (level == 0) {
|
2020-04-17 15:00:05 +02:00
|
|
|
result->add(elem);
|
2017-03-05 09:03:09 +01:00
|
|
|
} else {
|
2020-04-17 15:00:05 +02:00
|
|
|
result->add(copy(elem, level - 1));
|
2017-03-05 09:03:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
} else if (from_type == Element::map) {
|
|
|
|
ElementPtr result = ElementPtr(new MapElement());
|
2020-04-17 15:00:05 +02:00
|
|
|
for (auto kv : from->mapValue()) {
|
|
|
|
auto key = kv.first;
|
|
|
|
auto value = kv.second;
|
2017-03-05 09:03:09 +01:00
|
|
|
if (level == 0) {
|
2020-04-17 15:00:05 +02:00
|
|
|
result->set(key, value);
|
2017-03-05 09:03:09 +01:00
|
|
|
} else {
|
2020-04-17 15:00:05 +02:00
|
|
|
result->set(key, copy(value, level - 1));
|
2017-03-05 09:03:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
} else {
|
|
|
|
isc_throw(BadValue, "copy got an element of type: " << from_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Helper function which blocks infinite recursion
|
|
|
|
bool
|
2020-04-17 12:32:39 +03:00
|
|
|
isEquivalent0(ConstElementPtr a, ConstElementPtr b, unsigned level) {
|
2017-03-05 09:03:09 +01:00
|
|
|
// check looping forever on cycles
|
|
|
|
if (!level) {
|
|
|
|
isc_throw(BadValue, "isEquivalent got infinite recursion: "
|
|
|
|
"arguments include cycles");
|
|
|
|
}
|
|
|
|
if (!a || !b) {
|
|
|
|
isc_throw(BadValue, "isEquivalent got a null pointer");
|
|
|
|
}
|
|
|
|
// check types
|
|
|
|
if (a->getType() != b->getType()) {
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
if (a->getType() == Element::list) {
|
|
|
|
// check empty
|
|
|
|
if (a->empty()) {
|
|
|
|
return (b->empty());
|
|
|
|
}
|
|
|
|
// check size
|
|
|
|
if (a->size() != b->size()) {
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy b into a list
|
|
|
|
const size_t s = a->size();
|
2020-04-17 16:48:50 +03:00
|
|
|
std::list<ConstElementPtr> l;
|
2017-03-05 09:03:09 +01:00
|
|
|
for (size_t i = 0; i < s; ++i) {
|
|
|
|
l.push_back(b->get(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
// iterate on a
|
|
|
|
for (size_t i = 0; i < s; ++i) {
|
|
|
|
ConstElementPtr item = a->get(i);
|
|
|
|
// lookup this item in the list
|
|
|
|
bool found = false;
|
2020-04-17 15:22:11 +03:00
|
|
|
for (auto it = l.begin(); it != l.end(); ++it) {
|
2017-03-05 09:03:09 +01:00
|
|
|
// if found in the list remove it
|
|
|
|
if (isEquivalent0(item, *it, level - 1)) {
|
|
|
|
found = true;
|
|
|
|
l.erase(it);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if not found argument differs
|
|
|
|
if (!found) {
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sanity check: the list must be empty
|
|
|
|
if (!l.empty()) {
|
|
|
|
isc_throw(Unexpected, "isEquivalent internal error");
|
|
|
|
}
|
|
|
|
return (true);
|
|
|
|
} else if (a->getType() == Element::map) {
|
2020-04-17 15:00:05 +02:00
|
|
|
// check sizes
|
2020-04-17 12:32:39 +03:00
|
|
|
if (a->size() != b->size()) {
|
|
|
|
return (false);
|
|
|
|
}
|
2020-04-17 15:00:05 +02:00
|
|
|
// iterate on the first map
|
2023-12-04 10:18:55 +01:00
|
|
|
for (const auto& kv : a->mapValue()) {
|
2017-03-05 09:03:09 +01:00
|
|
|
// get the b value for the given keyword and recurse
|
2020-04-17 15:00:05 +02:00
|
|
|
ConstElementPtr item = b->get(kv.first);
|
|
|
|
if (!item || !isEquivalent0(kv.second, item, level - 1)) {
|
2017-03-05 09:03:09 +01:00
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (true);
|
|
|
|
} else {
|
|
|
|
return (a->equals(*b));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 14:46:42 +03:00
|
|
|
} // end anonymous namespace
|
2017-03-05 09:03:09 +01:00
|
|
|
|
|
|
|
bool
|
|
|
|
isEquivalent(ConstElementPtr a, ConstElementPtr b) {
|
|
|
|
return (isEquivalent0(a, b, 100));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
prettyPrint(ConstElementPtr element, std::ostream& out,
|
|
|
|
unsigned indent, unsigned step) {
|
|
|
|
if (!element) {
|
|
|
|
isc_throw(BadValue, "prettyPrint got a null pointer");
|
|
|
|
}
|
|
|
|
if (element->getType() == Element::list) {
|
|
|
|
// empty list case
|
|
|
|
if (element->empty()) {
|
|
|
|
out << "[ ]";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// complex ? multiline : oneline
|
|
|
|
if (!element->get(0)) {
|
|
|
|
isc_throw(BadValue, "prettyPrint got a null pointer");
|
|
|
|
}
|
|
|
|
int first_type = element->get(0)->getType();
|
|
|
|
bool complex = false;
|
|
|
|
if ((first_type == Element::list) || (first_type == Element::map)) {
|
|
|
|
complex = true;
|
|
|
|
}
|
|
|
|
std::string separator = complex ? ",\n" : ", ";
|
|
|
|
|
|
|
|
// open the list
|
|
|
|
out << "[" << (complex ? "\n" : " ");
|
2017-08-18 10:27:29 +03:00
|
|
|
|
2017-03-05 09:03:09 +01:00
|
|
|
// iterate on items
|
2020-04-17 15:00:05 +02:00
|
|
|
const auto& l = element->listValue();
|
2020-04-17 15:22:11 +03:00
|
|
|
for (auto it = l.begin(); it != l.end(); ++it) {
|
2017-03-05 09:03:09 +01:00
|
|
|
// add the separator if not the first item
|
|
|
|
if (it != l.begin()) {
|
|
|
|
out << separator;
|
|
|
|
}
|
|
|
|
// add indentation
|
|
|
|
if (complex) {
|
|
|
|
out << std::string(indent + step, ' ');
|
|
|
|
}
|
|
|
|
// recursive call
|
|
|
|
prettyPrint(*it, out, indent + step, step);
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the list
|
|
|
|
if (complex) {
|
|
|
|
out << "\n" << std::string(indent, ' ');
|
|
|
|
} else {
|
|
|
|
out << " ";
|
|
|
|
}
|
|
|
|
out << "]";
|
|
|
|
} else if (element->getType() == Element::map) {
|
|
|
|
// empty map case
|
|
|
|
if (element->size() == 0) {
|
|
|
|
out << "{ }";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// open the map
|
|
|
|
out << "{\n";
|
|
|
|
|
|
|
|
// iterate on keyword: value
|
2020-04-17 15:00:05 +02:00
|
|
|
const auto& m = element->mapValue();
|
2020-04-11 20:21:13 +02:00
|
|
|
bool first = true;
|
2020-04-17 15:22:11 +03:00
|
|
|
for (auto it = m.begin(); it != m.end(); ++it) {
|
2017-03-05 09:03:09 +01:00
|
|
|
// add the separator if not the first item
|
2017-11-28 01:57:48 +01:00
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
} else {
|
2017-03-05 09:03:09 +01:00
|
|
|
out << ",\n";
|
|
|
|
}
|
|
|
|
// add indentation
|
|
|
|
out << std::string(indent + step, ' ');
|
|
|
|
// add keyword:
|
|
|
|
out << "\"" << it->first << "\": ";
|
2017-07-23 16:01:58 -04:00
|
|
|
// recursive call
|
2017-03-05 09:03:09 +01:00
|
|
|
prettyPrint(it->second, out, indent + step, step);
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the map
|
|
|
|
out << "\n" << std::string(indent, ' ') << "}";
|
|
|
|
} else {
|
|
|
|
// not a list or a map
|
|
|
|
element->toJSON(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) {
|
|
|
|
std::stringstream ss;
|
|
|
|
prettyPrint(element, ss, indent, step);
|
|
|
|
return (ss.str());
|
|
|
|
}
|
|
|
|
|
2014-05-19 14:15:29 +02:00
|
|
|
void Element::preprocess(std::istream& in, std::stringstream& out) {
|
2014-05-08 16:01:07 +02:00
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
|
|
|
while (std::getline(in, line)) {
|
|
|
|
// If this is a comments line, replace it with empty line
|
|
|
|
// (so the line numbers will still match
|
|
|
|
if (!line.empty() && line[0] == '#') {
|
|
|
|
line = "";
|
|
|
|
}
|
|
|
|
|
2017-01-24 20:07:17 +01:00
|
|
|
// getline() removes end line characters. Unfortunately, we need
|
2014-05-08 16:01:07 +02:00
|
|
|
// it for getting the line numbers right (in case we report an
|
|
|
|
// error.
|
2014-05-19 14:15:29 +02:00
|
|
|
out << line;
|
|
|
|
out << "\n";
|
2014-05-08 16:01:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 14:46:42 +03:00
|
|
|
} // end of isc::data namespace
|
|
|
|
} // end of isc namespace
|