kun yu 77e1ddd81b branch-0.4.0
Former-commit-id: a4df63653202df32d0b983de27f5c969905d17ac
2019-07-30 10:23:34 +08:00

960 lines
26 KiB
C++

// Copyright 2017 Daniel Parker
// Distributed under the Boost license, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// See https://github.com/danielaparker/jsoncons for latest version
#ifndef JSONCONS_JSONPOINTER_JSONPOINTER_HPP
#define JSONCONS_JSONPOINTER_JSONPOINTER_HPP
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <iostream>
#include <iterator>
#include <utility> // std::move
#include <system_error> // system_error
#include <type_traits> // std::enable_if, std::true_type
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpointer/jsonpointer_error.hpp>
#include <jsoncons/detail/print_number.hpp>
namespace jsoncons { namespace jsonpointer {
// find_by_reference
template <class J, class Enable=void>
struct is_accessible_by_reference : std::false_type {};
template <class J>
struct is_accessible_by_reference<J,
typename std::enable_if<std::is_reference<decltype(std::declval<J>().at(typename J::string_view_type()))>::value
&& std::is_reference<decltype(std::declval<J>().at(0))>::value>::type>
: std::true_type {};
namespace detail {
enum class pointer_state
{
start,
escaped,
delim
};
} // detail
// address_iterator
template <class InputIt>
class address_iterator
{
typedef typename std::iterator_traits<InputIt>::value_type char_type;
typedef std::basic_string<char_type> string_type;
typedef InputIt base_iterator;
base_iterator path_ptr_;
base_iterator end_input_;
base_iterator p_;
base_iterator q_;
jsonpointer::detail::pointer_state state_;
size_t line_;
size_t column_;
std::basic_string<char_type> buffer_;
public:
typedef string_type value_type;
typedef std::ptrdiff_t difference_type;
typedef value_type* pointer;
typedef const value_type& reference;
typedef std::input_iterator_tag iterator_category;
address_iterator(base_iterator first, base_iterator last)
: address_iterator(first, last, first)
{
std::error_code ec;
increment(ec);
}
address_iterator(base_iterator first, base_iterator last, base_iterator current)
: path_ptr_(first), end_input_(last), p_(current), q_(current), state_(jsonpointer::detail::pointer_state::start)
{
}
address_iterator(const address_iterator&) = default;
address_iterator(address_iterator&&) = default;
address_iterator& operator=(const address_iterator&) = default;
address_iterator& operator=(address_iterator&&) = default;
address_iterator& operator++()
{
std::error_code ec;
increment(ec);
if (ec)
{
throw jsonpointer_error(ec);
}
return *this;
}
address_iterator& increment(std::error_code& ec)
{
q_ = p_;
buffer_.clear();
bool done = false;
while (p_ != end_input_ && !done)
{
switch (state_)
{
case jsonpointer::detail::pointer_state::start:
switch (*p_)
{
case '/':
state_ = jsonpointer::detail::pointer_state::delim;
break;
default:
ec = jsonpointer_errc::expected_slash;
done = true;
break;
};
break;
case jsonpointer::detail::pointer_state::delim:
switch (*p_)
{
case '/':
state_ = jsonpointer::detail::pointer_state::delim;
done = true;
break;
case '~':
state_ = jsonpointer::detail::pointer_state::escaped;
break;
default:
buffer_.push_back(*p_);
break;
};
break;
case jsonpointer::detail::pointer_state::escaped:
switch (*p_)
{
case '0':
buffer_.push_back('~');
state_ = jsonpointer::detail::pointer_state::delim;
break;
case '1':
buffer_.push_back('/');
state_ = jsonpointer::detail::pointer_state::delim;
break;
default:
ec = jsonpointer_errc::expected_0_or_1;
done = true;
break;
};
break;
default:
JSONCONS_UNREACHABLE();
break;
}
++p_;
++column_;
}
return *this;
}
address_iterator operator++(int) // postfix increment
{
address_iterator temp(*this);
++(*this);
return temp;
}
reference operator*() const
{
return buffer_;
}
friend bool operator==(const address_iterator& it1, const address_iterator& it2)
{
return it1.q_ == it2.q_;
}
friend bool operator!=(const address_iterator& it1, const address_iterator& it2)
{
return !(it1 == it2);
}
private:
};
template <class CharT>
std::basic_string<CharT> escape_string(const std::basic_string<CharT>& s)
{
std::basic_string<CharT> result;
for (auto c : s)
{
switch (c)
{
case '~':
result.push_back('~');
result.push_back('0');
break;
case '/':
result.push_back('~');
result.push_back('1');
break;
default:
result.push_back(c);
break;
}
}
return result;
}
// address
template <class CharT>
class basic_address
{
public:
std::basic_string<CharT> path_;
public:
// Member types
typedef CharT char_type;
typedef std::basic_string<char_type> string_type;
typedef basic_string_view<char_type> string_view_type;
typedef address_iterator<typename string_type::const_iterator> const_iterator;
typedef const_iterator iterator;
// Constructors
basic_address()
{
}
explicit basic_address(const string_type& s)
: path_(s)
{
}
explicit basic_address(string_type&& s)
: path_(std::move(s))
{
}
explicit basic_address(const CharT* s)
: path_(s)
{
}
basic_address(const basic_address&) = default;
basic_address(basic_address&&) = default;
// operator=
basic_address& operator=(const basic_address&) = default;
basic_address& operator=(basic_address&&) = default;
// Modifiers
void clear()
{
path_.clear();
}
basic_address& operator/=(const string_type& s)
{
path_.push_back('/');
path_.append(escape_string(s));
return *this;
}
basic_address& operator+=(const basic_address& p)
{
path_.append(p.path_);
return *this;
}
// Accessors
bool empty() const
{
return path_.empty();
}
const string_type& string() const
{
return path_;
}
operator string_view_type() const
{
return path_;
}
// Iterators
iterator begin() const
{
return iterator(path_.begin(),path_.end());
}
iterator end() const
{
return iterator(path_.begin(), path_.end(), path_.end());
}
// Non-member functions
friend basic_address<CharT> operator/(const basic_address<CharT>& lhs, const string_type& rhs)
{
basic_address<CharT> p(lhs);
p /= rhs;
return p;
}
friend basic_address<CharT> operator+( const basic_address<CharT>& lhs, const basic_address<CharT>& rhs )
{
basic_address<CharT> p(lhs);
p += rhs;
return p;
}
friend bool operator==( const basic_address& lhs, const basic_address& rhs )
{
return lhs.path_ == rhs.path_;
}
friend bool operator!=( const basic_address& lhs, const basic_address& rhs )
{
return lhs.path_ != rhs.path_;
}
friend std::basic_ostream<CharT>&
operator<<( std::basic_ostream<CharT>& os, const basic_address<CharT>& p )
{
os << p.path_;
return os;
}
};
typedef basic_address<char> address;
namespace detail {
template <class J,class JReference,class Enable = void>
class handle_type
{
public:
using value_type = typename J::value_type;
using type = value_type;
handle_type(const value_type& val) noexcept
: val_(val)
{
}
handle_type(value_type&& val) noexcept
: val_(std::move(val))
{
}
handle_type(const handle_type& w) noexcept
: val_(w.val_)
{
}
handle_type& operator=(const handle_type&) noexcept = default;
type get() const noexcept
{
return val_;
}
private:
value_type val_;
};
template <class J,class JReference>
class handle_type<J,JReference,
typename std::enable_if<is_accessible_by_reference<J>::value>::type>
{
public:
using reference = JReference;
using type = reference;
using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JReference>::type>::value,typename J::const_pointer,typename J::pointer>::type;
handle_type(reference ref) noexcept
: ptr_(std::addressof(ref))
{
}
handle_type(const handle_type&) noexcept = default;
handle_type& operator=(const handle_type&) noexcept = default;
type get() const noexcept
{
return *ptr_;
}
private:
pointer ptr_;
};
template<class J,class JReference>
class jsonpointer_evaluator : public ser_context
{
typedef typename handle_type<J,JReference>::type type;
typedef typename J::string_type string_type;
typedef typename string_type::value_type char_type;
typedef typename J::string_view_type string_view_type;
using reference = JReference;
using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JReference>::type>::value,typename J::const_pointer,typename J::pointer>::type;
size_t line_;
size_t column_;
string_type buffer_;
std::vector<handle_type<J,JReference>> current_;
public:
type get_result()
{
return current_.back().get();
}
void get(reference root, const string_view_type& path, std::error_code& ec)
{
evaluate(root, path, ec);
if (ec)
{
return;
}
if (path.empty())
{
return;
}
resolve(current_, buffer_, ec);
}
string_type normalized_path(reference root, const string_view_type& path)
{
std::error_code ec;
evaluate(root, path, ec);
if (ec)
{
return string_type(path);
}
if (current_.back().get().is_array() && buffer_.size() == 1 && buffer_[0] == '-')
{
string_type p = string_type(path.substr(0,path.length()-1));
std::string s = std::to_string(current_.back().get().size());
for (auto c : s)
{
p.push_back(c);
}
return p;
}
else
{
return string_type(path);
}
}
void insert_or_assign(reference root, const string_view_type& path, const J& value, std::error_code& ec)
{
evaluate(root, path, ec);
if (ec)
{
return;
}
if (current_.back().get().is_array())
{
if (buffer_.size() == 1 && buffer_[0] == '-')
{
current_.back().get().push_back(value);
}
else
{
if (!jsoncons::detail::is_integer(buffer_.data(), buffer_.length()))
{
ec = jsonpointer_errc::invalid_index;
return;
}
auto result = jsoncons::detail::to_integer<size_t>(buffer_.data(), buffer_.length());
if (result.ec != jsoncons::detail::to_integer_errc())
{
ec = jsonpointer_errc::invalid_index;
return;
}
size_t index = result.value;
if (index > current_.back().get().size())
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
if (index == current_.back().get().size())
{
current_.back().get().push_back(value);
}
else
{
current_.back().get().insert(current_.back().get().array_range().begin()+index,value);
}
}
}
else if (current_.back().get().is_object())
{
current_.back().get().insert_or_assign(buffer_,value);
}
else
{
ec = jsonpointer_errc::expected_object_or_array;
return;
}
}
void insert(reference root, const string_view_type& path, const J& value, std::error_code& ec)
{
evaluate(root, path, ec);
if (ec)
{
return;
}
if (current_.back().get().is_array())
{
if (buffer_.size() == 1 && buffer_[0] == '-')
{
current_.back().get().push_back(value);
}
else
{
if (!jsoncons::detail::is_integer(buffer_.data(), buffer_.length()))
{
ec = jsonpointer_errc::invalid_index;
return;
}
auto result = jsoncons::detail::to_integer<size_t>(buffer_.data(), buffer_.length());
if (result.ec != jsoncons::detail::to_integer_errc())
{
ec = jsonpointer_errc::invalid_index;
return;
}
size_t index = result.value;
if (index > current_.back().get().size())
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
if (index == current_.back().get().size())
{
current_.back().get().push_back(value);
}
else
{
current_.back().get().insert(current_.back().get().array_range().begin()+index,value);
}
}
}
else if (current_.back().get().is_object())
{
if (current_.back().get().contains(buffer_))
{
ec = jsonpointer_errc::key_already_exists;
return;
}
else
{
current_.back().get().insert_or_assign(buffer_,value);
}
}
else
{
ec = jsonpointer_errc::expected_object_or_array;
return;
}
}
void remove(reference root, const string_view_type& path, std::error_code& ec)
{
evaluate(root, path, ec);
if (ec)
{
return;
}
if (current_.back().get().is_array())
{
if (buffer_.size() == 1 && buffer_[0] == '-')
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
else
{
if (!jsoncons::detail::is_integer(buffer_.data(), buffer_.length()))
{
ec = jsonpointer_errc::invalid_index;
return;
}
auto result = jsoncons::detail::to_integer<size_t>(buffer_.data(), buffer_.length());
if (result.ec != jsoncons::detail::to_integer_errc())
{
ec = jsonpointer_errc::invalid_index;
return;
}
size_t index = result.value;
if (index >= current_.back().get().size())
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
current_.back().get().erase(current_.back().get().array_range().begin()+index);
}
}
else if (current_.back().get().is_object())
{
if (!current_.back().get().contains(buffer_))
{
ec = jsonpointer_errc::name_not_found;
return;
}
else
{
current_.back().get().erase(buffer_);
}
}
else
{
ec = jsonpointer_errc::expected_object_or_array;
return;
}
}
void replace(reference root, const string_view_type& path, const J& value, std::error_code& ec)
{
evaluate(root, path, ec);
if (ec)
{
return;
}
if (current_.back().get().is_array())
{
if (buffer_.size() == 1 && buffer_[0] == '-')
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
else
{
if (!jsoncons::detail::is_integer(buffer_.data(), buffer_.length()))
{
ec = jsonpointer_errc::invalid_index;
return;
}
auto result = jsoncons::detail::to_integer<size_t>(buffer_.data(), buffer_.length());
if (result.ec != jsoncons::detail::to_integer_errc())
{
ec = jsonpointer_errc::invalid_index;
return;
}
size_t index = result.value;
if (index >= current_.back().get().size())
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
(current_.back().get())[index] = value;
}
}
else if (current_.back().get().is_object())
{
if (!current_.back().get().contains(buffer_))
{
ec = jsonpointer_errc::key_already_exists;
return;
}
else
{
current_.back().get().insert_or_assign(buffer_,value);
}
}
else
{
ec = jsonpointer_errc::expected_object_or_array;
return;
}
}
void evaluate(reference root, const string_view_type& path, std::error_code& ec)
{
current_.push_back(root);
address_iterator<typename string_view_type::iterator> it(path.begin(), path.end());
address_iterator<typename string_view_type::iterator> end(path.begin(), path.end(), path.end());
while (it != end)
{
buffer_ = *it;
it.increment(ec);
if (ec)
return;
if (it == end)
{
return;
}
resolve(current_, buffer_, ec);
if (ec)
return;
}
}
static void resolve(std::vector<handle_type<J,JReference>>& current,
const string_view_type& buffer,
std::error_code& ec)
{
if (current.back().get().is_array())
{
if (buffer.size() == 1 && buffer[0] == '-')
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
else
{
if (!jsoncons::detail::is_integer(buffer.data(), buffer.length()))
{
ec = jsonpointer_errc::invalid_index;
return;
}
auto result = jsoncons::detail::to_integer<size_t>(buffer.data(), buffer.length());
if (result.ec != jsoncons::detail::to_integer_errc())
{
ec = jsonpointer_errc::invalid_index;
return;
}
size_t index = result.value;
if (index >= current.back().get().size())
{
ec = jsonpointer_errc::index_exceeds_array_size;
return;
}
current.push_back(current.back().get().at(index));
}
}
else if (current.back().get().is_object())
{
if (!current.back().get().contains(buffer))
{
ec = jsonpointer_errc::name_not_found;
return;
}
current.push_back(current.back().get().at(buffer));
}
else
{
ec = jsonpointer_errc::expected_object_or_array;
return;
}
}
// ser_context
size_t line() const override
{
return line_;
}
size_t column() const override
{
return column_;
}
};
}
template<class J>
typename J::string_type normalized_path(const J& root, const typename J::string_view_type& path)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,const J&> evaluator;
return evaluator.normalized_path(root,path);
}
template<class J>
typename std::enable_if<is_accessible_by_reference<J>::value,J&>::type
get(J& root, const typename J::string_view_type& path)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
std::error_code ec;
evaluator.get(root, path, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
return evaluator.get_result();
}
template<class J>
typename std::enable_if<is_accessible_by_reference<J>::value,const J&>::type
get(const J& root, const typename J::string_view_type& path)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,const J&> evaluator;
std::error_code ec;
evaluator.get(root, path, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
return evaluator.get_result();
}
template<class J>
typename std::enable_if<!is_accessible_by_reference<J>::value,J>::type
get(const J& root, const typename J::string_view_type& path)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,const J&> evaluator;
std::error_code ec;
evaluator.get(root, path, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
return evaluator.get_result();
}
template<class J>
typename std::enable_if<is_accessible_by_reference<J>::value,J&>::type
get(J& root, const typename J::string_view_type& path, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
evaluator.get(root, path, ec);
return evaluator.get_result();
}
template<class J>
typename std::enable_if<is_accessible_by_reference<J>::value,const J&>::type
get(const J& root, const typename J::string_view_type& path, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,const J&> evaluator;
evaluator.get(root, path, ec);
return evaluator.get_result();
}
template<class J>
typename std::enable_if<!is_accessible_by_reference<J>::value,J>::type
get(const J& root, const typename J::string_view_type& path, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,const J&> evaluator;
evaluator.get(root, path, ec);
return evaluator.get_result();
}
template<class J>
bool contains(const J& root, const typename J::string_view_type& path)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,const J&> evaluator;
std::error_code ec;
evaluator.get(root, path, ec);
return !ec ? true : false;
}
template<class J>
void insert_or_assign(J& root, const typename J::string_view_type& path, const J& value)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
std::error_code ec;
evaluator.insert_or_assign(root, path, value, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
}
template<class J>
void insert_or_assign(J& root, const typename J::string_view_type& path, const J& value, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
evaluator.insert_or_assign(root, path, value, ec);
}
template<class J>
void insert(J& root, const typename J::string_view_type& path, const J& value)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
std::error_code ec;
evaluator.insert(root, path, value, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
}
template<class J>
void insert(J& root, const typename J::string_view_type& path, const J& value, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
evaluator.insert(root, path, value, ec);
}
template<class J>
void remove(J& root, const typename J::string_view_type& path)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
std::error_code ec;
evaluator.remove(root, path, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
}
template<class J>
void remove(J& root, const typename J::string_view_type& path, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
evaluator.remove(root, path, ec);
}
template<class J>
void replace(J& root, const typename J::string_view_type& path, const J& value)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
std::error_code ec;
evaluator.replace(root, path, value, ec);
if (ec)
{
JSONCONS_THROW(jsonpointer_error(ec));
}
}
template<class J>
void replace(J& root, const typename J::string_view_type& path, const J& value, std::error_code& ec)
{
jsoncons::jsonpointer::detail::jsonpointer_evaluator<J,J&> evaluator;
evaluator.replace(root, path, value, ec);
}
template <class String>
void escape(const String& s, std::basic_ostringstream<typename String::value_type>& os)
{
for (auto c : s)
{
if (c == '~')
{
os.put('~');
os.put('0');
}
else if (c == '/')
{
os.put('~');
os.put('1');
}
else
{
os.put(c);
}
}
}
}}
#endif