mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2026-06-21 17:27:09 +02:00
Major plugin refactor and cleanup.
Switched to POCO library for unified platform/library interface. Deprecated the external module API. It was creating more problems than solving. Removed most built-in libraries in favor of system libraries for easier maintenance. Cleaned and secured code with help from static analyzers.
This commit is contained in:
Vendored
+266
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// Array.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Array
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Array.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
#include "Poco/JSONString.h"
|
||||
|
||||
|
||||
using Poco::Dynamic::Var;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
Array::Array(int options):
|
||||
_modified(false),
|
||||
_escapeUnicode((options & Poco::JSON_ESCAPE_UNICODE) != 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Array::Array(const Array& other) :
|
||||
_values(other._values),
|
||||
_pArray(other._pArray),
|
||||
_modified(other._modified),
|
||||
_escapeUnicode(other._escapeUnicode)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Array::Array(Array&& other) noexcept:
|
||||
_values(std::move(other._values)),
|
||||
_pArray(std::move(other._pArray)),
|
||||
_modified(other._modified),
|
||||
_escapeUnicode(other._escapeUnicode)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Array& Array::operator = (const Array& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
_values = other._values;
|
||||
_pArray = other._pArray;
|
||||
_modified = other._modified;
|
||||
_escapeUnicode = other._escapeUnicode;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Array& Array::operator = (Array&& other) noexcept
|
||||
{
|
||||
_values = std::move(other._values);
|
||||
_pArray = std::move(other._pArray);
|
||||
_modified = other._modified;
|
||||
_escapeUnicode = other._escapeUnicode;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Array::~Array()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Var Array::get(unsigned int index) const
|
||||
{
|
||||
Var value;
|
||||
try
|
||||
{
|
||||
value = _values.at(index);
|
||||
}
|
||||
catch (std::out_of_range&)
|
||||
{
|
||||
//Ignore, we return an empty value
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Array::Ptr Array::getArray(unsigned int index) const
|
||||
{
|
||||
Array::Ptr result;
|
||||
|
||||
Var value = get(index);
|
||||
if (value.type() == typeid(Array::Ptr))
|
||||
{
|
||||
result = value.extract<Array::Ptr>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Object::Ptr Array::getObject(unsigned int index) const
|
||||
{
|
||||
Object::Ptr result;
|
||||
|
||||
Var value = get(index);
|
||||
if (value.type() == typeid(Object::Ptr))
|
||||
{
|
||||
result = value.extract<Object::Ptr>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Array::isNull(unsigned int index) const
|
||||
{
|
||||
if (index < _values.size())
|
||||
{
|
||||
Dynamic::Var value = _values[index];
|
||||
return value.isEmpty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Array::isObject(unsigned int index) const
|
||||
{
|
||||
Var value = get(index);
|
||||
return isObject(value);
|
||||
}
|
||||
|
||||
|
||||
bool Array::isObject(const Dynamic::Var& value) const
|
||||
{
|
||||
return value.type() == typeid(Object::Ptr);
|
||||
}
|
||||
|
||||
|
||||
bool Array::isObject(ConstIterator& it) const
|
||||
{
|
||||
return it!= end() && isObject(*it);
|
||||
}
|
||||
|
||||
|
||||
void Array::stringify(std::ostream& out, unsigned int indent, int step) const
|
||||
{
|
||||
int options = Poco::JSON_WRAP_STRINGS;
|
||||
options |= _escapeUnicode ? Poco::JSON_ESCAPE_UNICODE : 0;
|
||||
|
||||
if (step == -1) step = indent;
|
||||
|
||||
out << "[";
|
||||
|
||||
if (indent > 0) out << std::endl;
|
||||
|
||||
for (ValueVec::const_iterator it = _values.begin(); it != _values.end();)
|
||||
{
|
||||
for (int i = 0; i < indent; i++) out << ' ';
|
||||
|
||||
Stringifier::stringify(*it, out, indent + step, step, options);
|
||||
|
||||
if (++it != _values.end())
|
||||
{
|
||||
out << ",";
|
||||
if (step > 0) out << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (step > 0) out << '\n';
|
||||
|
||||
if (indent >= step) indent -= step;
|
||||
|
||||
for (int i = 0; i < indent; i++) out << ' ';
|
||||
|
||||
out << "]";
|
||||
}
|
||||
|
||||
|
||||
void Array::resetDynArray() const
|
||||
{
|
||||
if (!_pArray)
|
||||
_pArray = new Poco::Dynamic::Array;
|
||||
else
|
||||
_pArray->clear();
|
||||
}
|
||||
|
||||
|
||||
Array::operator const Poco::Dynamic::Array& () const
|
||||
{
|
||||
if (!_values.size())
|
||||
{
|
||||
resetDynArray();
|
||||
}
|
||||
else if (_modified)
|
||||
{
|
||||
ValueVec::const_iterator it = _values.begin();
|
||||
ValueVec::const_iterator end = _values.end();
|
||||
resetDynArray();
|
||||
int index = 0;
|
||||
for (; it != end; ++it, ++index)
|
||||
{
|
||||
if (isObject(it))
|
||||
{
|
||||
_pArray->insert(_pArray->end(), Poco::JSON::Object::makeStruct(getObject(index)));
|
||||
}
|
||||
else if (isArray(it))
|
||||
{
|
||||
_pArray->insert(_pArray->end(), makeArray(getArray(index)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_pArray->insert(_pArray->end(), *it);
|
||||
}
|
||||
}
|
||||
_modified = false;
|
||||
}
|
||||
|
||||
return *_pArray;
|
||||
}
|
||||
|
||||
|
||||
Poco::Dynamic::Array Array::makeArray(const JSON::Array::Ptr& arr)
|
||||
{
|
||||
Poco::Dynamic::Array vec;
|
||||
|
||||
JSON::Array::ConstIterator it = arr->begin();
|
||||
JSON::Array::ConstIterator end = arr->end();
|
||||
int index = 0;
|
||||
for (; it != end; ++it, ++index)
|
||||
{
|
||||
if (arr->isObject(it))
|
||||
{
|
||||
Object::Ptr pObj = arr->getObject(index);
|
||||
DynamicStruct str = Poco::JSON::Object::makeStruct(pObj);
|
||||
vec.insert(vec.end(), str);
|
||||
}
|
||||
else if (arr->isArray(it))
|
||||
{
|
||||
Array::Ptr pArr = arr->getArray(index);
|
||||
std::vector<Poco::Dynamic::Var> v = makeArray(pArr);
|
||||
vec.insert(vec.end(), v);
|
||||
}
|
||||
else
|
||||
vec.insert(vec.end(), *it);
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
void Array::clear()
|
||||
{
|
||||
_values.clear();
|
||||
_pArray = 0;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Handler.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Handler
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Handler.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
Handler::Handler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Handler::~Handler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Dynamic::Var Handler::asVar() const
|
||||
{
|
||||
return Dynamic::Var();
|
||||
}
|
||||
|
||||
|
||||
Poco::DynamicStruct Handler::asStruct() const
|
||||
{
|
||||
return Poco::DynamicStruct();
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// JSONException.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: JSONException
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/JSONException.h"
|
||||
#include <typeinfo>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
POCO_IMPLEMENT_EXCEPTION(JSONException, Exception, "JSON Exception")
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+342
@@ -0,0 +1,342 @@
|
||||
//
|
||||
// Object.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Object
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using Poco::Dynamic::Var;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
Object::Object(int options):
|
||||
_preserveInsOrder((options & Poco::JSON_PRESERVE_KEY_ORDER) != 0),
|
||||
_escapeUnicode((options & Poco::JSON_ESCAPE_UNICODE) != 0),
|
||||
_modified(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Object::Object(const Object& other) : _values(other._values),
|
||||
_preserveInsOrder(other._preserveInsOrder),
|
||||
_escapeUnicode(other._escapeUnicode),
|
||||
_pStruct(!other._modified ? other._pStruct : 0),
|
||||
_modified(other._modified)
|
||||
{
|
||||
syncKeys(other._keys);
|
||||
}
|
||||
|
||||
|
||||
Object::Object(Object&& other) noexcept:
|
||||
_values(std::move(other._values)),
|
||||
_keys(std::move(other._keys)),
|
||||
_preserveInsOrder(other._preserveInsOrder),
|
||||
_escapeUnicode(other._escapeUnicode),
|
||||
_pStruct(std::move(other._pStruct)),
|
||||
_pOrdStruct(std::move(other._pOrdStruct)),
|
||||
_modified(other._modified)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Object &Object::operator = (const Object &other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
_values = other._values;
|
||||
_keys = other._keys;
|
||||
_preserveInsOrder = other._preserveInsOrder;
|
||||
_escapeUnicode = other._escapeUnicode;
|
||||
_pStruct = !other._modified ? other._pStruct : 0;
|
||||
_modified = other._modified;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Object& Object::operator = (Object&& other) noexcept
|
||||
{
|
||||
_values = std::move(other._values);
|
||||
_keys = std::move(other._keys);
|
||||
_preserveInsOrder = other._preserveInsOrder;
|
||||
_escapeUnicode = other._escapeUnicode;
|
||||
_pStruct = std::move(other._pStruct);
|
||||
_pOrdStruct = std::move(other._pOrdStruct);
|
||||
_modified = other._modified;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void Object::syncKeys(const KeyList& keys)
|
||||
{
|
||||
if(_preserveInsOrder)
|
||||
{
|
||||
// update iterators in _keys to point to copied _values
|
||||
for(KeyList::const_iterator it = keys.begin(); it != keys.end(); ++it)
|
||||
{
|
||||
ValueMap::const_iterator itv = _values.find((*it)->first);
|
||||
poco_assert (itv != _values.end());
|
||||
_keys.push_back(itv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Var Object::get(const std::string& key) const
|
||||
{
|
||||
ValueMap::const_iterator it = _values.find(key);
|
||||
if (it != _values.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return Var();
|
||||
}
|
||||
|
||||
|
||||
Array::Ptr Object::getArray(const std::string& key) const
|
||||
{
|
||||
ValueMap::const_iterator it = _values.find(key);
|
||||
if ((it != _values.end()) && (it->second.type() == typeid(Array::Ptr)))
|
||||
{
|
||||
return it->second.extract<Array::Ptr>();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Object::Ptr Object::getObject(const std::string& key) const
|
||||
{
|
||||
ValueMap::const_iterator it = _values.find(key);
|
||||
if ((it != _values.end()) && (it->second.type() == typeid(Object::Ptr)))
|
||||
{
|
||||
return it->second.extract<Object::Ptr>();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Object::getNames(NameList& names) const
|
||||
{
|
||||
names.clear();
|
||||
if (_preserveInsOrder)
|
||||
{
|
||||
for(KeyList::const_iterator it = _keys.begin(); it != _keys.end(); ++it)
|
||||
{
|
||||
names.push_back((*it)->first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(ValueMap::const_iterator it = _values.begin(); it != _values.end(); ++it)
|
||||
{
|
||||
names.push_back(it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Object::NameList Object::getNames() const
|
||||
{
|
||||
NameList names;
|
||||
getNames(names);
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
void Object::stringify(std::ostream& out, unsigned int indent, int step) const
|
||||
{
|
||||
if (step < 0) step = indent;
|
||||
|
||||
if (!_preserveInsOrder)
|
||||
doStringify(_values, out, indent, step);
|
||||
else
|
||||
doStringify(_keys, out, indent, step);
|
||||
}
|
||||
|
||||
|
||||
const std::string& Object::getKey(KeyList::const_iterator& iter) const
|
||||
{
|
||||
ValueMap::const_iterator it = _values.begin();
|
||||
ValueMap::const_iterator end = _values.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (it == *iter) return it->first;
|
||||
}
|
||||
|
||||
throw NotFoundException((*iter)->first);
|
||||
}
|
||||
|
||||
|
||||
Object& Object::set(const std::string& key, const Dynamic::Var& value)
|
||||
{
|
||||
std::pair<ValueMap::iterator, bool> ret = _values.insert(ValueMap::value_type(key, value));
|
||||
if (!ret.second) ret.first->second = value;
|
||||
if (_preserveInsOrder)
|
||||
{
|
||||
KeyList::iterator it = _keys.begin();
|
||||
KeyList::iterator end = _keys.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (key == (*it)->first) return *this;
|
||||
}
|
||||
_keys.push_back(ret.first);
|
||||
}
|
||||
_modified = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Poco::DynamicStruct Object::makeStruct(const Object::Ptr& obj)
|
||||
{
|
||||
return makeStructImpl<Poco::DynamicStruct>(obj);
|
||||
}
|
||||
|
||||
|
||||
Poco::OrderedDynamicStruct Object::makeOrderedStruct(const Object::Ptr& obj)
|
||||
{
|
||||
return makeStructImpl<Poco::OrderedDynamicStruct>(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
void Object::resetOrdDynStruct() const
|
||||
{
|
||||
if (!_pOrdStruct)
|
||||
_pOrdStruct = new Poco::OrderedDynamicStruct;
|
||||
else
|
||||
_pOrdStruct->clear();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
void Object::resetDynStruct() const
|
||||
{
|
||||
if (!_pStruct)
|
||||
_pStruct = new Poco::DynamicStruct;
|
||||
else
|
||||
_pStruct->clear();
|
||||
}*/
|
||||
|
||||
|
||||
Object::operator const Poco::DynamicStruct& () const
|
||||
{
|
||||
if (!_values.size())
|
||||
{
|
||||
resetDynStruct(_pStruct);
|
||||
}
|
||||
else if (_modified)
|
||||
{
|
||||
ValueMap::const_iterator it = _values.begin();
|
||||
ValueMap::const_iterator end = _values.end();
|
||||
resetDynStruct(_pStruct);
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (isObject(it))
|
||||
{
|
||||
_pStruct->insert(it->first, makeStruct(getObject(it->first)));
|
||||
}
|
||||
else if (isArray(it))
|
||||
{
|
||||
_pStruct->insert(it->first, Poco::JSON::Array::makeArray(getArray(it->first)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_pStruct->insert(it->first, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *_pStruct;
|
||||
}
|
||||
|
||||
|
||||
Object::operator const Poco::OrderedDynamicStruct& () const
|
||||
{
|
||||
if (!_values.size())
|
||||
{
|
||||
resetDynStruct(_pOrdStruct);
|
||||
}
|
||||
else if (_modified)
|
||||
{
|
||||
if (_preserveInsOrder)
|
||||
{
|
||||
KeyList::const_iterator it = _keys.begin();
|
||||
KeyList::const_iterator end = _keys.end();
|
||||
resetDynStruct(_pOrdStruct);
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (isObject((*it)->first))
|
||||
{
|
||||
_pOrdStruct->insert((*it)->first, makeOrderedStruct(getObject((*it)->first)));
|
||||
}
|
||||
else if (isArray((*it)->first))
|
||||
{
|
||||
_pOrdStruct->insert((*it)->first, Poco::JSON::Array::makeArray(getArray((*it)->first)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_pOrdStruct->insert((*it)->first, (*it)->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ValueMap::const_iterator it = _values.begin();
|
||||
ValueMap::const_iterator end = _values.end();
|
||||
resetDynStruct(_pOrdStruct);
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (isObject(it))
|
||||
{
|
||||
_pOrdStruct->insert(it->first, makeOrderedStruct(getObject(it->first)));
|
||||
}
|
||||
else if (isArray(it))
|
||||
{
|
||||
_pOrdStruct->insert(it->first, Poco::JSON::Array::makeArray(getArray(it->first)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_pOrdStruct->insert(it->first, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *_pOrdStruct;
|
||||
}
|
||||
|
||||
|
||||
void Object::clear()
|
||||
{
|
||||
_values.clear();
|
||||
_keys.clear();
|
||||
_pStruct = 0;
|
||||
_modified = true;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// ParseHandler.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: ParseHandler
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/ParseHandler.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/JSON/JSONException.h"
|
||||
|
||||
|
||||
using Poco::Dynamic::Var;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
ParseHandler::ParseHandler(bool preserveObjectOrder) : Handler(),
|
||||
_preserveObjectOrder(preserveObjectOrder)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ParseHandler::~ParseHandler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::reset()
|
||||
{
|
||||
while (!_stack.empty()) _stack.pop();
|
||||
_key = "";
|
||||
_result.empty();
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::startObject()
|
||||
{
|
||||
Object::Ptr newObj = new Object(_preserveObjectOrder);
|
||||
if (_stack.empty()) // The first object
|
||||
{
|
||||
_result = newObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
Var parent = _stack.top();
|
||||
|
||||
if (parent.type() == typeid(Array::Ptr))
|
||||
{
|
||||
Array::Ptr arr = parent.extract<Array::Ptr>();
|
||||
arr->add(newObj);
|
||||
}
|
||||
else if (parent.type() == typeid(Object::Ptr))
|
||||
{
|
||||
poco_assert_dbg(!_key.empty());
|
||||
Object::Ptr obj = parent.extract<Object::Ptr>();
|
||||
obj->set(_key, newObj);
|
||||
_key.clear();
|
||||
}
|
||||
}
|
||||
|
||||
_stack.push(newObj);
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::endObject()
|
||||
{
|
||||
if (!_stack.empty()) _stack.pop();
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::startArray()
|
||||
{
|
||||
Array::Ptr newArr = new Array();
|
||||
|
||||
if (_stack.empty()) // The first array
|
||||
{
|
||||
_result = newArr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Var parent = _stack.top();
|
||||
|
||||
if (parent.type() == typeid(Array::Ptr))
|
||||
{
|
||||
Array::Ptr arr = parent.extract<Array::Ptr>();
|
||||
arr->add(newArr);
|
||||
}
|
||||
else if (parent.type() == typeid(Object::Ptr))
|
||||
{
|
||||
poco_assert_dbg(!_key.empty());
|
||||
Object::Ptr obj = parent.extract<Object::Ptr>();
|
||||
obj->set(_key, newArr);
|
||||
_key.clear();
|
||||
}
|
||||
}
|
||||
|
||||
_stack.push(newArr);
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::endArray()
|
||||
{
|
||||
if (!_stack.empty()) _stack.pop();
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::key(const std::string& k)
|
||||
{
|
||||
_key = k;
|
||||
}
|
||||
|
||||
|
||||
void ParseHandler::setValue(const Var& value)
|
||||
{
|
||||
if (_stack.size())
|
||||
{
|
||||
Var parent = _stack.top();
|
||||
|
||||
if (parent.type() == typeid(Array::Ptr))
|
||||
{
|
||||
Array::Ptr arr = parent.extract<Array::Ptr>();
|
||||
arr->add(value);
|
||||
}
|
||||
else if (parent.type() == typeid(Object::Ptr))
|
||||
{
|
||||
Object::Ptr obj = parent.extract<Object::Ptr>();
|
||||
obj->set(_key, value);
|
||||
_key.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JSONException("Attempt to set value on an empty stack");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Parser.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Parser
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/JSON/JSONException.h"
|
||||
#include "Poco/Ascii.h"
|
||||
#include "Poco/Token.h"
|
||||
#include "Poco/UTF8Encoding.h"
|
||||
#include "Poco/String.h"
|
||||
#undef min
|
||||
#undef max
|
||||
#include <limits>
|
||||
#include <clocale>
|
||||
#include <istream>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
Parser::Parser(const Handler::Ptr& pHandler, std::size_t bufSize):
|
||||
ParserImpl(pHandler, bufSize)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Parser::~Parser()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Parser::setHandler(const Handler::Ptr& pHandler)
|
||||
{
|
||||
setHandlerImpl(pHandler);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+241
@@ -0,0 +1,241 @@
|
||||
//
|
||||
// Parser.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Parser
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/JSON/JSONException.h"
|
||||
#include "Poco/Ascii.h"
|
||||
#include "Poco/Token.h"
|
||||
#include "Poco/UTF8Encoding.h"
|
||||
#include "Poco/String.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#undef min
|
||||
#undef max
|
||||
#include <limits>
|
||||
#include <clocale>
|
||||
#include <istream>
|
||||
#include "pdjson.h"
|
||||
|
||||
|
||||
typedef struct json_stream json_stream;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
ParserImpl::ParserImpl(const Handler::Ptr& pHandler, std::size_t bufSize):
|
||||
_pJSON(new json_stream),
|
||||
_pHandler(pHandler),
|
||||
_depth(JSON_UNLIMITED_DEPTH),
|
||||
_decimalPoint('.'),
|
||||
_allowNullByte(true),
|
||||
_allowComments(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ParserImpl::~ParserImpl()
|
||||
{
|
||||
delete _pJSON;
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::handle(const std::string& json)
|
||||
{
|
||||
if (!_allowNullByte && json.find("\\u0000") != json.npos)
|
||||
throw JSONException("Null bytes in strings not allowed.");
|
||||
|
||||
try
|
||||
{
|
||||
json_open_buffer(_pJSON, json.data(), json.size());
|
||||
checkError();
|
||||
//////////////////////////////////
|
||||
// Underlying parser is capable of parsing multiple consecutive JSONs;
|
||||
// we do not currently support this feature; to force error on
|
||||
// excessive characters past valid JSON end, this MUST be called
|
||||
// AFTER opening the buffer - otherwise it is overwritten by
|
||||
// json_open*() call, which calls internal init()
|
||||
json_set_streaming(_pJSON, false);
|
||||
/////////////////////////////////
|
||||
handle(); checkError();
|
||||
if (JSON_DONE != json_next(_pJSON))
|
||||
throw JSONException("Excess characters found after JSON end.");
|
||||
json_close(_pJSON);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
json_close(_pJSON);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Dynamic::Var ParserImpl::parseImpl(const std::string& json)
|
||||
{
|
||||
if (_allowComments)
|
||||
{
|
||||
std::string str = json;
|
||||
stripComments(str);
|
||||
handle(str);
|
||||
}
|
||||
else handle(json);
|
||||
|
||||
return asVarImpl();
|
||||
}
|
||||
|
||||
|
||||
Dynamic::Var ParserImpl::parseImpl(std::istream& in)
|
||||
{
|
||||
std::ostringstream os;
|
||||
StreamCopier::copyStream(in, os);
|
||||
return parseImpl(os.str());
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::stripComments(std::string& json)
|
||||
{
|
||||
if (_allowComments)
|
||||
{
|
||||
bool inString = false;
|
||||
bool inComment = false;
|
||||
char prevChar = 0;
|
||||
std::string::iterator it = json.begin();
|
||||
for (; it != json.end();)
|
||||
{
|
||||
if (*it == '"' && !inString) inString = true;
|
||||
else inString = false;
|
||||
if (!inString)
|
||||
{
|
||||
if (*it == '/' && it + 1 != json.end() && *(it + 1) == '*')
|
||||
inComment = true;
|
||||
}
|
||||
if (inComment)
|
||||
{
|
||||
char c = *it;
|
||||
it = json.erase(it);
|
||||
if (prevChar == '*' && c == '/')
|
||||
{
|
||||
inComment = false;
|
||||
prevChar = 0;
|
||||
}
|
||||
else prevChar = c;
|
||||
}
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::handleArray()
|
||||
{
|
||||
json_type tok = json_peek(_pJSON);
|
||||
while (tok != JSON_ARRAY_END && checkError())
|
||||
{
|
||||
handle();
|
||||
tok = json_peek(_pJSON);
|
||||
}
|
||||
|
||||
if (tok == JSON_ARRAY_END) handle();
|
||||
else throw JSONException("JSON array end not found");
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::handleObject()
|
||||
{
|
||||
json_type tok = json_peek(_pJSON);
|
||||
while (tok != JSON_OBJECT_END && checkError())
|
||||
{
|
||||
json_next(_pJSON);
|
||||
if (_pHandler) _pHandler->key(std::string(json_get_string(_pJSON, NULL)));
|
||||
handle();
|
||||
tok = json_peek(_pJSON);
|
||||
}
|
||||
|
||||
if (tok == JSON_OBJECT_END) handle();
|
||||
else throw JSONException("JSON object end not found");
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::handle()
|
||||
{
|
||||
enum json_type type = json_next(_pJSON);
|
||||
switch (type)
|
||||
{
|
||||
case JSON_DONE:
|
||||
return;
|
||||
case JSON_NULL:
|
||||
_pHandler->null();
|
||||
break;
|
||||
case JSON_TRUE:
|
||||
if (_pHandler) _pHandler->value(true);
|
||||
break;
|
||||
case JSON_FALSE:
|
||||
if (_pHandler) _pHandler->value(false);
|
||||
break;
|
||||
case JSON_NUMBER:
|
||||
{
|
||||
if (_pHandler)
|
||||
{
|
||||
std::string str(json_get_string(_pJSON, NULL));
|
||||
if (str.find(_decimalPoint) != str.npos || str.find('e') != str.npos || str.find('E') != str.npos)
|
||||
{
|
||||
_pHandler->value(NumberParser::parseFloat(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
Poco::Int64 val;
|
||||
if (NumberParser::tryParse64(str, val))
|
||||
_pHandler->value(val);
|
||||
else
|
||||
_pHandler->value(NumberParser::parseUnsigned64(str));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSON_STRING:
|
||||
if (_pHandler) _pHandler->value(std::string(json_get_string(_pJSON, NULL)));
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
if (_pHandler) _pHandler->startObject();
|
||||
handleObject();
|
||||
break;
|
||||
case JSON_OBJECT_END:
|
||||
if (_pHandler) _pHandler->endObject();
|
||||
return;
|
||||
case JSON_ARRAY:
|
||||
if (_pHandler) _pHandler->startArray();
|
||||
handleArray();
|
||||
break;
|
||||
case JSON_ARRAY_END:
|
||||
if (_pHandler) _pHandler->endArray();
|
||||
return;
|
||||
case JSON_ERROR:
|
||||
{
|
||||
const char* pErr = json_get_error(_pJSON);
|
||||
std::string err(pErr ? pErr : "JSON parser error.");
|
||||
throw JSONException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ParserImpl::checkError()
|
||||
{
|
||||
const char* err = json_get_error(_pJSON);
|
||||
if (err) throw Poco::JSON::JSONException(err);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// PrintHandler.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: PrintHandler
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/PrintHandler.h"
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
PrintHandler::PrintHandler(unsigned indent, int options):
|
||||
_out(std::cout),
|
||||
_indent(indent),
|
||||
_array(0),
|
||||
_objStart(true),
|
||||
_options(options)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PrintHandler::PrintHandler(std::ostream& out, unsigned indent, int options):
|
||||
_out(out),
|
||||
_indent(indent),
|
||||
_array(0),
|
||||
_objStart(true),
|
||||
_options(options)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PrintHandler::~PrintHandler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::reset()
|
||||
{
|
||||
_out.flush();
|
||||
_tab = "";
|
||||
_array = 0;
|
||||
_objStart = true;
|
||||
}
|
||||
|
||||
|
||||
const char* PrintHandler::endLine() const
|
||||
{
|
||||
if (!printFlat()) return "\n";
|
||||
else return "";
|
||||
}
|
||||
|
||||
|
||||
bool PrintHandler::printFlat() const
|
||||
{
|
||||
return _indent == JSON_PRINT_FLAT;
|
||||
}
|
||||
|
||||
|
||||
unsigned PrintHandler::indent()
|
||||
{
|
||||
if (!printFlat()) return _indent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::startObject()
|
||||
{
|
||||
arrayValue();
|
||||
_out << '{';
|
||||
_out << endLine();
|
||||
_tab.append(indent(), ' ');
|
||||
_objStart = true;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::endObject()
|
||||
{
|
||||
if (_tab.length() >= indent())
|
||||
_tab.erase(_tab.length() - indent());
|
||||
|
||||
_out << endLine() << _tab << '}';
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::startArray()
|
||||
{
|
||||
arrayValue();
|
||||
_out << '[' << endLine();
|
||||
_tab.append(indent(), ' ');
|
||||
++_array;
|
||||
_objStart = true;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::endArray()
|
||||
{
|
||||
_tab.erase(_tab.length() - indent());
|
||||
_out << endLine() << _tab << ']';
|
||||
--_array;
|
||||
poco_assert (_array >= 0);
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::key(const std::string& k)
|
||||
{
|
||||
if (!_objStart) comma();
|
||||
|
||||
_objStart = true;
|
||||
|
||||
_out << _tab;
|
||||
Stringifier::formatString(k, _out, _options);
|
||||
if (!printFlat()) _out << ' ';
|
||||
_out << ':';
|
||||
if (!printFlat()) _out << ' ';
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::null()
|
||||
{
|
||||
arrayValue();
|
||||
_out << "null";
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::value(int v)
|
||||
{
|
||||
arrayValue();
|
||||
_out << v;
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::value(unsigned v)
|
||||
{
|
||||
arrayValue();
|
||||
_out << v;
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
#if defined(POCO_HAVE_INT64)
|
||||
void PrintHandler::value(Int64 v)
|
||||
{
|
||||
arrayValue();
|
||||
_out << v;
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::value(UInt64 v)
|
||||
{
|
||||
arrayValue();
|
||||
_out << v;
|
||||
_objStart = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void PrintHandler::value(const std::string& value)
|
||||
{
|
||||
arrayValue();
|
||||
Stringifier::formatString(value, _out, _options);
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::value(double d)
|
||||
{
|
||||
arrayValue();
|
||||
_out << d;
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::value(bool b)
|
||||
{
|
||||
arrayValue();
|
||||
_out << b;
|
||||
_objStart = false;
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::comma()
|
||||
{
|
||||
_out << ',' << endLine();
|
||||
}
|
||||
|
||||
|
||||
void PrintHandler::arrayValue()
|
||||
{
|
||||
if (!_objStart) comma();
|
||||
if (array())
|
||||
{
|
||||
_out << _tab;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+172
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// Query.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Query
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Query.h"
|
||||
#include "Poco/StringTokenizer.h"
|
||||
#include "Poco/RegularExpression.h"
|
||||
#include "Poco/NumberParser.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using Poco::Dynamic::Var;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
Query::Query(const Var& source): _source(source)
|
||||
{
|
||||
if (!source.isEmpty() &&
|
||||
source.type() != typeid(Object) &&
|
||||
source.type() != typeid(Object::Ptr) &&
|
||||
source.type() != typeid(Array) &&
|
||||
source.type() != typeid(Array::Ptr))
|
||||
throw InvalidArgumentException("Only JSON Object, Array or pointers thereof allowed.");
|
||||
}
|
||||
|
||||
|
||||
Query::~Query()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Object::Ptr Query::findObject(const std::string& path) const
|
||||
{
|
||||
Var result = find(path);
|
||||
|
||||
if (result.type() == typeid(Object::Ptr))
|
||||
return result.extract<Object::Ptr>();
|
||||
else if (result.type() == typeid(Object))
|
||||
return new Object(result.extract<Object>());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Object& Query::findObject(const std::string& path, Object& obj) const
|
||||
{
|
||||
obj.clear();
|
||||
|
||||
Var result = find(path);
|
||||
|
||||
if (result.type() == typeid(Object::Ptr))
|
||||
obj = *result.extract<Object::Ptr>();
|
||||
else if (result.type() == typeid(Object))
|
||||
obj = result.extract<Object>();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
Array::Ptr Query::findArray(const std::string& path) const
|
||||
{
|
||||
Var result = find(path);
|
||||
|
||||
if (result.type() == typeid(Array::Ptr))
|
||||
return result.extract<Array::Ptr>();
|
||||
else if (result.type() == typeid(Array))
|
||||
return new Array(result.extract<Array>());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Array& Query::findArray(const std::string& path, Array& arr) const
|
||||
{
|
||||
arr.clear();
|
||||
|
||||
Var result = find(path);
|
||||
|
||||
if (result.type() == typeid(Array::Ptr))
|
||||
arr = *result.extract<Array::Ptr>();
|
||||
else if (result.type() == typeid(Array))
|
||||
arr = result.extract<Array>();
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
Var Query::find(const std::string& path) const
|
||||
{
|
||||
Var result = _source;
|
||||
StringTokenizer tokenizer(path, ".");
|
||||
for (const auto& token: tokenizer)
|
||||
{
|
||||
if (!result.isEmpty())
|
||||
{
|
||||
std::vector<int> indexes;
|
||||
RegularExpression::MatchVec matches;
|
||||
int firstOffset = -1;
|
||||
int offset = 0;
|
||||
RegularExpression regex("\\[([0-9]+)\\]");
|
||||
while (regex.match(token, offset, matches) > 0)
|
||||
{
|
||||
if (firstOffset == -1)
|
||||
{
|
||||
firstOffset = static_cast<int>(matches[0].offset);
|
||||
}
|
||||
std::string num(token, matches[1].offset, matches[1].length);
|
||||
indexes.push_back(NumberParser::parse(num));
|
||||
offset = static_cast<int>(matches[0].offset + matches[0].length);
|
||||
}
|
||||
|
||||
std::string name(token);
|
||||
if (firstOffset != -1)
|
||||
{
|
||||
name = name.substr(0, firstOffset);
|
||||
}
|
||||
|
||||
if (name.length() > 0)
|
||||
{
|
||||
if (result.type() == typeid(Object::Ptr))
|
||||
{
|
||||
Object::Ptr o = result.extract<Object::Ptr>();
|
||||
result = o->get(name);
|
||||
}
|
||||
else if (result.type() == typeid(Object))
|
||||
{
|
||||
Object o = result.extract<Object>();
|
||||
result = o.get(name);
|
||||
}
|
||||
else
|
||||
result.empty();
|
||||
|
||||
}
|
||||
|
||||
if (!result.isEmpty() && !indexes.empty())
|
||||
{
|
||||
for (auto i: indexes)
|
||||
{
|
||||
if (result.type() == typeid(Array::Ptr))
|
||||
{
|
||||
Array::Ptr array = result.extract<Array::Ptr>();
|
||||
result = array->get(i);
|
||||
if (result.isEmpty()) break;
|
||||
}
|
||||
else if (result.type() == typeid(Array))
|
||||
{
|
||||
Array array = result.extract<Array>();
|
||||
result = array.get(i);
|
||||
if (result.isEmpty()) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Stringifier.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Stringifier
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
#include "Poco/JSON/Array.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
using Poco::Dynamic::Var;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
void Stringifier::stringify(const Var& any, std::ostream& out, unsigned int indent, int step, int options)
|
||||
{
|
||||
bool escapeUnicode = ((options & Poco::JSON_ESCAPE_UNICODE) != 0);
|
||||
|
||||
if (step == -1) step = indent;
|
||||
|
||||
if (any.type() == typeid(Object))
|
||||
{
|
||||
Object& o = const_cast<Object&>(any.extract<Object>());
|
||||
o.setEscapeUnicode(escapeUnicode);
|
||||
o.stringify(out, indent == 0 ? 0 : indent, step);
|
||||
}
|
||||
else if (any.type() == typeid(Array))
|
||||
{
|
||||
Array& a = const_cast<Array&>(any.extract<Array>());
|
||||
a.setEscapeUnicode(escapeUnicode);
|
||||
a.stringify(out, indent == 0 ? 0 : indent, step);
|
||||
}
|
||||
else if (any.type() == typeid(Object::Ptr))
|
||||
{
|
||||
Object::Ptr& o = const_cast<Object::Ptr&>(any.extract<Object::Ptr>());
|
||||
o->setEscapeUnicode(escapeUnicode);
|
||||
o->stringify(out, indent == 0 ? 0 : indent, step);
|
||||
}
|
||||
else if (any.type() == typeid(Array::Ptr))
|
||||
{
|
||||
Array::Ptr& a = const_cast<Array::Ptr&>(any.extract<Array::Ptr>());
|
||||
a->setEscapeUnicode(escapeUnicode);
|
||||
a->stringify(out, indent == 0 ? 0 : indent, step);
|
||||
}
|
||||
else if (any.isEmpty())
|
||||
{
|
||||
out << "null";
|
||||
}
|
||||
else if (any.isNumeric() || any.isBoolean())
|
||||
{
|
||||
std::string value = any.convert<std::string>();
|
||||
if (any.type() == typeid(char)) formatString(value, out, options);
|
||||
else out << value;
|
||||
}
|
||||
else if (any.isString() || any.isDateTime() || any.isDate() || any.isTime())
|
||||
{
|
||||
std::string value = any.convert<std::string>();
|
||||
formatString(value, out, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
out << any.convert<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Stringifier::formatString(const std::string& value, std::ostream& out, int options)
|
||||
{
|
||||
Poco::toJSON(value, out, options);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
Vendored
+701
@@ -0,0 +1,701 @@
|
||||
//
|
||||
// Template.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: Template
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/JSON/Template.h"
|
||||
#include "Poco/JSON/TemplateCache.h"
|
||||
#include "Poco/JSON/Query.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/FileStream.h"
|
||||
|
||||
|
||||
using Poco::Dynamic::Var;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
POCO_IMPLEMENT_EXCEPTION(JSONTemplateException, Exception, "Template Exception")
|
||||
|
||||
|
||||
class Part
|
||||
{
|
||||
public:
|
||||
Part()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Part()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void render(const Var& data, std::ostream& out) const = 0;
|
||||
|
||||
typedef std::vector<SharedPtr<Part>> VectorParts;
|
||||
};
|
||||
|
||||
|
||||
class StringPart: public Part
|
||||
{
|
||||
public:
|
||||
StringPart(): Part()
|
||||
{
|
||||
}
|
||||
|
||||
StringPart(const std::string& content): Part(), _content(content)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~StringPart()
|
||||
{
|
||||
}
|
||||
|
||||
void render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
out << _content;
|
||||
}
|
||||
|
||||
void setContent(const std::string& content)
|
||||
{
|
||||
_content = content;
|
||||
}
|
||||
|
||||
inline std::string getContent() const
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _content;
|
||||
};
|
||||
|
||||
|
||||
class MultiPart: public Part
|
||||
{
|
||||
public:
|
||||
MultiPart()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MultiPart()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void addPart(Part* part)
|
||||
{
|
||||
_parts.push_back(part);
|
||||
}
|
||||
|
||||
void render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
for (const auto& p: _parts)
|
||||
{
|
||||
p->render(data, out);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
VectorParts _parts;
|
||||
};
|
||||
|
||||
|
||||
class EchoPart: public Part
|
||||
{
|
||||
public:
|
||||
EchoPart(const std::string& query): Part(), _query(query)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~EchoPart()
|
||||
{
|
||||
}
|
||||
|
||||
void render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
Query query(data);
|
||||
Var value = query.find(_query);
|
||||
|
||||
if (!value.isEmpty())
|
||||
{
|
||||
out << value.convert<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _query;
|
||||
};
|
||||
|
||||
|
||||
class LogicQuery
|
||||
{
|
||||
public:
|
||||
LogicQuery(const std::string& query): _queryString(query)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~LogicQuery()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool apply(const Var& data) const
|
||||
{
|
||||
bool logic = false;
|
||||
|
||||
Query query(data);
|
||||
Var value = query.find(_queryString);
|
||||
|
||||
if (!value.isEmpty()) // When empty, logic will be false
|
||||
{
|
||||
if (value.isString())
|
||||
// An empty string must result in false, otherwise true
|
||||
// Which is not the case when we convert to bool with Var
|
||||
{
|
||||
std::string s = value.convert<std::string>();
|
||||
logic = !s.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
// All other values, try to convert to bool
|
||||
// An empty object or array will turn into false
|
||||
// all other values depend on the convert<> in Var
|
||||
logic = value.convert<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
return logic;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _queryString;
|
||||
};
|
||||
|
||||
|
||||
class LogicExistQuery: public LogicQuery
|
||||
{
|
||||
public:
|
||||
LogicExistQuery(const std::string& query): LogicQuery(query)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~LogicExistQuery()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool apply(const Var& data) const
|
||||
{
|
||||
Query query(data);
|
||||
Var value = query.find(_queryString);
|
||||
|
||||
return !value.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class LogicElseQuery: public LogicQuery
|
||||
{
|
||||
public:
|
||||
LogicElseQuery(): LogicQuery("")
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~LogicElseQuery()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool apply(const Var& data) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class LogicPart: public MultiPart
|
||||
{
|
||||
public:
|
||||
LogicPart(): MultiPart()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~LogicPart()
|
||||
{
|
||||
}
|
||||
|
||||
void addPart(LogicQuery* query, Part* part)
|
||||
{
|
||||
MultiPart::addPart(part);
|
||||
_queries.push_back(query);
|
||||
}
|
||||
|
||||
void addPart(Part* part)
|
||||
{
|
||||
MultiPart::addPart(part);
|
||||
_queries.push_back(new LogicElseQuery());
|
||||
}
|
||||
|
||||
void render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
int count = 0;
|
||||
for (auto it = _queries.begin(); it != _queries.end(); ++it, ++count)
|
||||
{
|
||||
if ((*it)->apply(data) && _parts.size() > count)
|
||||
{
|
||||
_parts[count]->render(data, out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<SharedPtr<LogicQuery>> _queries;
|
||||
};
|
||||
|
||||
|
||||
class LoopPart: public MultiPart
|
||||
{
|
||||
public:
|
||||
LoopPart(const std::string& name, const std::string& query): MultiPart(), _name(name), _query(query)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~LoopPart()
|
||||
{
|
||||
}
|
||||
|
||||
void render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
Query query(data);
|
||||
|
||||
if (data.type() == typeid(Object::Ptr))
|
||||
{
|
||||
Object::Ptr dataObject = data.extract<Object::Ptr>();
|
||||
Array::Ptr array = query.findArray(_query);
|
||||
if (!array.isNull())
|
||||
{
|
||||
for (int i = 0; i < array->size(); i++)
|
||||
{
|
||||
Var value = array->get(i);
|
||||
dataObject->set(_name, value);
|
||||
MultiPart::render(data, out);
|
||||
}
|
||||
dataObject->remove(_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
std::string _query;
|
||||
};
|
||||
|
||||
|
||||
class IncludePart: public Part
|
||||
{
|
||||
public:
|
||||
|
||||
IncludePart(const Path& parentPath, const Path& path):
|
||||
Part(),
|
||||
_path(path)
|
||||
{
|
||||
// When the path is relative, try to make it absolute based
|
||||
// on the path of the parent template. When the file doesn't
|
||||
// exist, we keep it relative and hope that the cache can
|
||||
// resolve it.
|
||||
if (_path.isRelative())
|
||||
{
|
||||
Path templatePath(parentPath, _path);
|
||||
File templateFile(templatePath);
|
||||
if (templateFile.exists())
|
||||
{
|
||||
_path = templatePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~IncludePart()
|
||||
{
|
||||
}
|
||||
|
||||
void render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
TemplateCache* cache = TemplateCache::instance();
|
||||
if (cache == 0)
|
||||
{
|
||||
Template tpl(_path);
|
||||
tpl.parse();
|
||||
tpl.render(data, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
Template::Ptr tpl = cache->getTemplate(_path);
|
||||
tpl->render(data, out);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Path _path;
|
||||
};
|
||||
|
||||
|
||||
Template::Template(const Path& templatePath):
|
||||
_parts(0),
|
||||
_currentPart(0),
|
||||
_templatePath(templatePath)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Template::Template():
|
||||
_parts(0),
|
||||
_currentPart(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Template::~Template()
|
||||
{
|
||||
delete _parts;
|
||||
}
|
||||
|
||||
|
||||
void Template::parse()
|
||||
{
|
||||
File file(_templatePath);
|
||||
if (file.exists())
|
||||
{
|
||||
FileInputStream fis(_templatePath.toString());
|
||||
parse(fis);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Template::parse(std::istream& in)
|
||||
{
|
||||
_parseTime.update();
|
||||
|
||||
_parts = new MultiPart;
|
||||
_currentPart = _parts;
|
||||
|
||||
while (in.good())
|
||||
{
|
||||
std::string text = readText(in); // Try to read text first
|
||||
if (text.length() > 0)
|
||||
{
|
||||
_currentPart->addPart(new StringPart(text));
|
||||
}
|
||||
|
||||
if (in.bad())
|
||||
break; // Nothing to do anymore
|
||||
|
||||
std::string command = readTemplateCommand(in); // Try to read a template command
|
||||
if (command.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
readWhiteSpace(in);
|
||||
|
||||
if (command.compare("echo") == 0)
|
||||
{
|
||||
std::string query = readQuery(in);
|
||||
if (query.empty())
|
||||
{
|
||||
throw JSONTemplateException("Missing query in <? echo ?>");
|
||||
}
|
||||
_currentPart->addPart(new EchoPart(query));
|
||||
}
|
||||
else if (command.compare("for") == 0)
|
||||
{
|
||||
std::string loopVariable = readWord(in);
|
||||
if (loopVariable.empty())
|
||||
{
|
||||
throw JSONTemplateException("Missing variable in <? for ?> command");
|
||||
}
|
||||
readWhiteSpace(in);
|
||||
|
||||
std::string query = readQuery(in);
|
||||
if (query.empty())
|
||||
{
|
||||
throw JSONTemplateException("Missing query in <? for ?> command");
|
||||
}
|
||||
|
||||
_partStack.push(_currentPart);
|
||||
LoopPart* part = new LoopPart(loopVariable, query);
|
||||
_partStack.push(part);
|
||||
_currentPart->addPart(part);
|
||||
_currentPart = part;
|
||||
}
|
||||
else if (command.compare("else") == 0)
|
||||
{
|
||||
if (_partStack.size() == 0)
|
||||
{
|
||||
throw JSONTemplateException("Unexpected <? else ?> found");
|
||||
}
|
||||
_currentPart = _partStack.top();
|
||||
LogicPart* lp = dynamic_cast<LogicPart*>(_currentPart);
|
||||
if (lp == 0)
|
||||
{
|
||||
throw JSONTemplateException("Missing <? if ?> or <? ifexist ?> for <? else ?>");
|
||||
}
|
||||
MultiPart* part = new MultiPart();
|
||||
lp->addPart(part);
|
||||
_currentPart = part;
|
||||
}
|
||||
else if (command.compare("elsif") == 0 || command.compare("elif") == 0)
|
||||
{
|
||||
std::string query = readQuery(in);
|
||||
if (query.empty())
|
||||
{
|
||||
throw JSONTemplateException("Missing query in <? " + command + " ?>");
|
||||
}
|
||||
|
||||
if (_partStack.size() == 0)
|
||||
{
|
||||
throw JSONTemplateException("Unexpected <? elsif / elif ?> found");
|
||||
}
|
||||
|
||||
_currentPart = _partStack.top();
|
||||
LogicPart* lp = dynamic_cast<LogicPart*>(_currentPart);
|
||||
if (lp == 0)
|
||||
{
|
||||
throw JSONTemplateException("Missing <? if ?> or <? ifexist ?> for <? elsif / elif ?>");
|
||||
}
|
||||
MultiPart* part = new MultiPart();
|
||||
lp->addPart(new LogicQuery(query), part);
|
||||
_currentPart = part;
|
||||
}
|
||||
else if (command.compare("endfor") == 0)
|
||||
{
|
||||
if (_partStack.size() < 2)
|
||||
{
|
||||
throw JSONTemplateException("Unexpected <? endfor ?> found");
|
||||
}
|
||||
MultiPart* loopPart = _partStack.top();
|
||||
LoopPart* lp = dynamic_cast<LoopPart*>(loopPart);
|
||||
if (lp == 0)
|
||||
{
|
||||
throw JSONTemplateException("Missing <? for ?> command");
|
||||
}
|
||||
_partStack.pop();
|
||||
_currentPart = _partStack.top();
|
||||
_partStack.pop();
|
||||
}
|
||||
else if (command.compare("endif") == 0)
|
||||
{
|
||||
if (_partStack.size() < 2)
|
||||
{
|
||||
throw JSONTemplateException("Unexpected <? endif ?> found");
|
||||
}
|
||||
|
||||
_currentPart = _partStack.top();
|
||||
LogicPart* lp = dynamic_cast<LogicPart*>(_currentPart);
|
||||
if (lp == 0)
|
||||
{
|
||||
throw JSONTemplateException("Missing <? if ?> or <? ifexist ?> for <? endif ?>");
|
||||
}
|
||||
|
||||
_partStack.pop();
|
||||
_currentPart = _partStack.top();
|
||||
_partStack.pop();
|
||||
}
|
||||
else if (command.compare("if") == 0 || command.compare("ifexist") == 0)
|
||||
{
|
||||
std::string query = readQuery(in);
|
||||
if (query.empty())
|
||||
{
|
||||
throw JSONTemplateException("Missing query in <? " + command + " ?>");
|
||||
}
|
||||
_partStack.push(_currentPart);
|
||||
LogicPart* lp = new LogicPart();
|
||||
_partStack.push(lp);
|
||||
_currentPart->addPart(lp);
|
||||
_currentPart = new MultiPart();
|
||||
if (command.compare("ifexist") == 0)
|
||||
{
|
||||
lp->addPart(new LogicExistQuery(query), _currentPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
lp->addPart(new LogicQuery(query), _currentPart);
|
||||
}
|
||||
}
|
||||
else if (command.compare("include") == 0)
|
||||
{
|
||||
readWhiteSpace(in);
|
||||
std::string filename = readString(in);
|
||||
if (filename.empty())
|
||||
{
|
||||
throw JSONTemplateException("Missing filename in <? include ?>");
|
||||
}
|
||||
else
|
||||
{
|
||||
Path resolvePath(_templatePath);
|
||||
resolvePath.makeParent();
|
||||
_currentPart->addPart(new IncludePart(resolvePath, filename));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JSONTemplateException("Unknown command " + command);
|
||||
}
|
||||
|
||||
readWhiteSpace(in);
|
||||
|
||||
int c = in.get();
|
||||
if (c == '?' && in.peek() == '>')
|
||||
{
|
||||
in.get(); // forget '>'
|
||||
|
||||
if (command.compare("echo") != 0)
|
||||
{
|
||||
if (in.peek() == '\r')
|
||||
{
|
||||
in.get();
|
||||
}
|
||||
if (in.peek() == '\n')
|
||||
{
|
||||
in.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JSONTemplateException("Missing ?>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string Template::readText(std::istream& in)
|
||||
{
|
||||
std::string text;
|
||||
int c = in.get();
|
||||
while (c != -1)
|
||||
{
|
||||
if (c == '<')
|
||||
{
|
||||
if (in.peek() == '?')
|
||||
{
|
||||
in.get(); // forget '?'
|
||||
break;
|
||||
}
|
||||
}
|
||||
text += c;
|
||||
|
||||
c = in.get();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
std::string Template::readTemplateCommand(std::istream& in)
|
||||
{
|
||||
std::string command;
|
||||
|
||||
readWhiteSpace(in);
|
||||
|
||||
int c = in.get();
|
||||
while (c != -1)
|
||||
{
|
||||
if (Ascii::isSpace(c))
|
||||
break;
|
||||
|
||||
if (c == '?' && in.peek() == '>')
|
||||
{
|
||||
in.putback(c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '=' && command.length() == 0)
|
||||
{
|
||||
command = "echo";
|
||||
break;
|
||||
}
|
||||
|
||||
command += c;
|
||||
|
||||
c = in.get();
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
std::string Template::readWord(std::istream& in)
|
||||
{
|
||||
std::string word;
|
||||
int c;
|
||||
while ((c = in.peek()) != -1 && !Ascii::isSpace(c))
|
||||
{
|
||||
in.get();
|
||||
word += c;
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
|
||||
std::string Template::readQuery(std::istream& in)
|
||||
{
|
||||
std::string word;
|
||||
int c;
|
||||
while ((c = in.get()) != -1)
|
||||
{
|
||||
if (c == '?' && in.peek() == '>')
|
||||
{
|
||||
in.putback(c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Ascii::isSpace(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
word += c;
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
|
||||
void Template::readWhiteSpace(std::istream& in)
|
||||
{
|
||||
int c;
|
||||
while ((c = in.peek()) != -1 && Ascii::isSpace(c))
|
||||
{
|
||||
in.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string Template::readString(std::istream& in)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
int c = in.get();
|
||||
if (c == '"')
|
||||
{
|
||||
while ((c = in.get()) != -1 && c != '"')
|
||||
{
|
||||
str += c;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
void Template::render(const Var& data, std::ostream& out) const
|
||||
{
|
||||
_parts->render(data, out);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::JSON
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// TemplateCache.cpp
|
||||
//
|
||||
// Library: JSON
|
||||
// Package: JSON
|
||||
// Module: TemplateCache
|
||||
//
|
||||
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/JSON/TemplateCache.h"
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
TemplateCache* TemplateCache::_pInstance = 0;
|
||||
|
||||
|
||||
TemplateCache::TemplateCache(): _pLogger(0)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
|
||||
TemplateCache::~TemplateCache()
|
||||
{
|
||||
_pInstance = 0;
|
||||
}
|
||||
|
||||
|
||||
void TemplateCache::setup()
|
||||
{
|
||||
poco_assert (_pInstance == 0);
|
||||
_pInstance = this;
|
||||
}
|
||||
|
||||
|
||||
Template::Ptr TemplateCache::getTemplate(const Path& path)
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_trace_f1(*_pLogger, "Trying to load %s", path.toString());
|
||||
}
|
||||
|
||||
Path templatePath = resolvePath(path);
|
||||
std::string templatePathname = templatePath.toString();
|
||||
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_trace_f1(*_pLogger, "Path resolved to %s", templatePathname);
|
||||
}
|
||||
|
||||
File templateFile(templatePathname);
|
||||
|
||||
Template::Ptr tpl;
|
||||
|
||||
std::map<std::string, Template::Ptr>::iterator it = _cache.find(templatePathname);
|
||||
if (it == _cache.end())
|
||||
{
|
||||
if (templateFile.exists())
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_information_f1(*_pLogger, "Loading template %s", templatePath.toString());
|
||||
}
|
||||
|
||||
tpl = new Template(templatePath);
|
||||
|
||||
try
|
||||
{
|
||||
tpl->parse();
|
||||
_cache[templatePathname] = tpl;
|
||||
}
|
||||
catch (JSONTemplateException& jte)
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_error_f2(*_pLogger, "Template %s contains an error: %s", templatePath.toString(), jte.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_error_f1(*_pLogger, "Template file %s doesn't exist", templatePath.toString());
|
||||
}
|
||||
throw FileNotFoundException(templatePathname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tpl = it->second;
|
||||
if (tpl->parseTime() < templateFile.getLastModified())
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_information_f1(*_pLogger, "Reloading template %s", templatePath.toString());
|
||||
}
|
||||
|
||||
tpl = new Template(templatePath);
|
||||
|
||||
try
|
||||
{
|
||||
tpl->parse();
|
||||
_cache[templatePathname] = tpl;
|
||||
}
|
||||
catch (JSONTemplateException& jte)
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_error_f2(*_pLogger, "Template %s contains an error: %s", templatePath.toString(), jte.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tpl;
|
||||
}
|
||||
|
||||
|
||||
Path TemplateCache::resolvePath(const Path& path) const
|
||||
{
|
||||
if (path.isAbsolute())
|
||||
return path;
|
||||
|
||||
for (const auto& p: _includePaths)
|
||||
{
|
||||
Path templatePath(p, path);
|
||||
|
||||
File templateFile(templatePath);
|
||||
if (templateFile.exists())
|
||||
{
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_trace_f2(*_pLogger, "%s template file resolved to %s", path.toString(), templatePath.toString());
|
||||
}
|
||||
return templatePath;
|
||||
}
|
||||
if (_pLogger)
|
||||
{
|
||||
poco_trace_f1(*_pLogger, "%s doesn't exist", templatePath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
throw FileNotFoundException(path.toString());
|
||||
}
|
||||
|
||||
|
||||
} } // Poco::JSON
|
||||
Vendored
+870
@@ -0,0 +1,870 @@
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include "pdjson.h"
|
||||
|
||||
#define JSON_FLAG_ERROR (1u << 0)
|
||||
#define JSON_FLAG_STREAMING (1u << 1)
|
||||
|
||||
|
||||
// patched for poco 1.8.x (VS 2008)
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
|
||||
#define json_error(json, format, ...) \
|
||||
if (!(json->flags & JSON_FLAG_ERROR)) { \
|
||||
json->flags |= JSON_FLAG_ERROR; \
|
||||
_snprintf_s(json->errmsg, sizeof(json->errmsg), _TRUNCATE,\
|
||||
"error: %lu: " format, \
|
||||
(unsigned long) json->lineno, \
|
||||
__VA_ARGS__); \
|
||||
} \
|
||||
|
||||
#else
|
||||
|
||||
#define json_error(json, format, ...) \
|
||||
if (!(json->flags & JSON_FLAG_ERROR)) { \
|
||||
json->flags |= JSON_FLAG_ERROR; \
|
||||
snprintf(json->errmsg, sizeof(json->errmsg), \
|
||||
"error: %lu: " format, \
|
||||
(unsigned long) json->lineno, \
|
||||
__VA_ARGS__); \
|
||||
} \
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define STACK_INC 4
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define strerror_r(err, buf, len) strerror_s(buf, len, err)
|
||||
#endif
|
||||
/*
|
||||
const char *json_typename[] = {
|
||||
[JSON_ERROR] = "ERROR",
|
||||
[JSON_DONE] = "DONE",
|
||||
[JSON_OBJECT] = "OBJECT",
|
||||
[JSON_OBJECT_END] = "OBJECT_END",
|
||||
[JSON_ARRAY] = "ARRAY",
|
||||
[JSON_ARRAY_END] = "ARRAY_END",
|
||||
[JSON_STRING] = "STRING",
|
||||
[JSON_NUMBER] = "NUMBER",
|
||||
[JSON_TRUE] = "TRUE",
|
||||
[JSON_FALSE] = "FALSE",
|
||||
[JSON_NULL] = "NULL",
|
||||
};
|
||||
*/
|
||||
struct json_stack {
|
||||
enum json_type type;
|
||||
long count;
|
||||
};
|
||||
|
||||
static void json_error_s(json_stream *json, int err)
|
||||
{
|
||||
char errbuf[1024] = {0};
|
||||
strerror_r(err, errbuf, sizeof(errbuf));
|
||||
json_error(json, "%s", errbuf);
|
||||
}
|
||||
|
||||
static enum json_type
|
||||
push(json_stream *json, enum json_type type)
|
||||
{
|
||||
json->stack_top++;
|
||||
|
||||
if (json->stack_top >= json->stack_size) {
|
||||
struct json_stack *stack;
|
||||
stack = (struct json_stack *) json->alloc.realloc(json->stack,
|
||||
(json->stack_size + STACK_INC) * sizeof(*json->stack));
|
||||
if (stack == NULL) {
|
||||
json_error_s(json, errno);
|
||||
return JSON_ERROR;
|
||||
}
|
||||
|
||||
json->stack_size += STACK_INC;
|
||||
json->stack = stack;
|
||||
}
|
||||
|
||||
json->stack[json->stack_top].type = type;
|
||||
json->stack[json->stack_top].count = 0;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static enum json_type
|
||||
pop(json_stream *json, int c, enum json_type expected)
|
||||
{
|
||||
if (json->stack == NULL || json->stack[json->stack_top].type != expected) {
|
||||
json_error(json, "unexpected byte, '%c'", c);
|
||||
return JSON_ERROR;
|
||||
}
|
||||
json->stack_top--;
|
||||
return expected == JSON_ARRAY ? JSON_ARRAY_END : JSON_OBJECT_END;
|
||||
}
|
||||
|
||||
static int buffer_peek(struct json_source *source)
|
||||
{
|
||||
if (source->position < source->source.buffer.length)
|
||||
return source->source.buffer.buffer[source->position];
|
||||
else
|
||||
return EOF;
|
||||
}
|
||||
|
||||
static int buffer_get(struct json_source *source)
|
||||
{
|
||||
int c = source->peek(source);
|
||||
source->position++;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int stream_get(struct json_source *source)
|
||||
{
|
||||
source->position++;
|
||||
return fgetc(source->source.stream.stream);
|
||||
}
|
||||
|
||||
static int stream_peek(struct json_source *source)
|
||||
{
|
||||
int c = fgetc(source->source.stream.stream);
|
||||
ungetc(c, source->source.stream.stream);
|
||||
return c;
|
||||
}
|
||||
|
||||
static void init(json_stream *json)
|
||||
{
|
||||
json->lineno = 1;
|
||||
json->flags = JSON_FLAG_STREAMING;
|
||||
json->errmsg[0] = '\0';
|
||||
json->ntokens = 0;
|
||||
json->next = (enum json_type) 0;
|
||||
|
||||
json->stack = NULL;
|
||||
json->stack_top = -1;
|
||||
json->stack_size = 0;
|
||||
|
||||
json->data.string = NULL;
|
||||
json->data.string_size = 0;
|
||||
json->data.string_fill = 0;
|
||||
json->source.position = 0;
|
||||
|
||||
json->alloc.malloc = malloc;
|
||||
json->alloc.realloc = realloc;
|
||||
json->alloc.free = free;
|
||||
}
|
||||
|
||||
static enum json_type
|
||||
is_match(json_stream *json, const char *pattern, enum json_type type)
|
||||
{
|
||||
for (const char *p = pattern; *p; p++)
|
||||
if (*p != json->source.get(&json->source))
|
||||
return JSON_ERROR;
|
||||
return type;
|
||||
}
|
||||
|
||||
static int pushchar(json_stream *json, int c)
|
||||
{
|
||||
if (json->data.string_fill == json->data.string_size) {
|
||||
size_t size = json->data.string_size * 2;
|
||||
char *buffer = (char*) json->alloc.realloc(json->data.string, size);
|
||||
if (buffer == NULL) {
|
||||
json_error_s(json, errno);
|
||||
return -1;
|
||||
} else {
|
||||
json->data.string_size = size;
|
||||
json->data.string = buffer;
|
||||
}
|
||||
}
|
||||
json->data.string[json->data.string_fill++] = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_string(json_stream *json)
|
||||
{
|
||||
json->data.string_fill = 0;
|
||||
if (json->data.string == NULL) {
|
||||
json->data.string_size = 1024;
|
||||
json->data.string = (char*) json->alloc.malloc(json->data.string_size);
|
||||
if (json->data.string == NULL) {
|
||||
json_error_s(json, errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
json->data.string[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encode_utf8(json_stream *json, unsigned long c)
|
||||
{
|
||||
if (c < 0x80UL) {
|
||||
return pushchar(json, c);
|
||||
} else if (c < 0x0800UL) {
|
||||
return !((pushchar(json, (c >> 6 & 0x1F) | 0xC0) == 0) &&
|
||||
(pushchar(json, (c >> 0 & 0x3F) | 0x80) == 0));
|
||||
} else if (c < 0x010000UL) {
|
||||
if (c >= 0xd800 && c <= 0xdfff) {
|
||||
json_error(json, "invalid codepoint %06lx", c);
|
||||
return -1;
|
||||
}
|
||||
return !((pushchar(json, (c >> 12 & 0x0F) | 0xE0) == 0) &&
|
||||
(pushchar(json, (c >> 6 & 0x3F) | 0x80) == 0) &&
|
||||
(pushchar(json, (c >> 0 & 0x3F) | 0x80) == 0));
|
||||
} else if (c < 0x110000UL) {
|
||||
return !((pushchar(json, (c >> 18 & 0x07) | 0xF0) == 0) &&
|
||||
(pushchar(json, (c >> 12 & 0x3F) | 0x80) == 0) &&
|
||||
(pushchar(json, (c >> 6 & 0x3F) | 0x80) == 0) &&
|
||||
(pushchar(json, (c >> 0 & 0x3F) | 0x80) == 0));
|
||||
} else {
|
||||
json_error(json, "can't encode UTF-8 for %06lx", c);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int hexchar(int c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a':
|
||||
case 'A': return 10;
|
||||
case 'b':
|
||||
case 'B': return 11;
|
||||
case 'c':
|
||||
case 'C': return 12;
|
||||
case 'd':
|
||||
case 'D': return 13;
|
||||
case 'e':
|
||||
case 'E': return 14;
|
||||
case 'f':
|
||||
case 'F': return 15;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static long
|
||||
read_unicode_cp(json_stream *json)
|
||||
{
|
||||
long cp = 0;
|
||||
int shift = 12;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
int c = json->source.get(&json->source);
|
||||
int hc;
|
||||
|
||||
if (c == EOF) {
|
||||
json_error(json, "%s", "unterminated string literal in unicode");
|
||||
return -1;
|
||||
} else if ((hc = hexchar(c)) == -1) {
|
||||
json_error(json, "bad escape unicode byte, '%c'", c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cp += hc * (1 << shift);
|
||||
shift -= 4;
|
||||
}
|
||||
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
static int read_unicode(json_stream *json)
|
||||
{
|
||||
long cp, h, l;
|
||||
|
||||
if ((cp = read_unicode_cp(json)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cp >= 0xd800 && cp <= 0xdbff) {
|
||||
/* This is the high portion of a surrogate pair; we need to read the
|
||||
* lower portion to get the codepoint
|
||||
*/
|
||||
h = cp;
|
||||
|
||||
int c = json->source.get(&json->source);
|
||||
if (c == EOF) {
|
||||
json_error(json, "%s", "unterminated string literal in unicode");
|
||||
return -1;
|
||||
} else if (c != '\\') {
|
||||
json_error(json, "invalid continuation for surrogate pair: '%c', "
|
||||
"expected '\\'", c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = json->source.get(&json->source);
|
||||
if (c == EOF) {
|
||||
json_error(json, "%s", "unterminated string literal in unicode");
|
||||
return -1;
|
||||
} else if (c != 'u') {
|
||||
json_error(json, "invalid continuation for surrogate pair: '%c', "
|
||||
"expected 'u'", c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((l = read_unicode_cp(json)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (l < 0xdc00 || l > 0xdfff) {
|
||||
json_error(json, "invalid surrogate pair continuation \\u%04lx out "
|
||||
"of range (dc00-dfff)", l);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cp = ((h - 0xd800) * 0x400) + ((l - 0xdc00) + 0x10000);
|
||||
} else if (cp >= 0xdc00 && cp <= 0xdfff) {
|
||||
json_error(json, "dangling surrogate \\u%04lx", cp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return encode_utf8(json, cp);
|
||||
}
|
||||
|
||||
int read_escaped(json_stream *json)
|
||||
{
|
||||
int c = json->source.get(&json->source);
|
||||
if (c == EOF) {
|
||||
json_error(json, "%s", "unterminated string literal in escape");
|
||||
return -1;
|
||||
} else if (c == 'u') {
|
||||
if (read_unicode(json) != 0)
|
||||
return -1;
|
||||
} else {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 't':
|
||||
case '/':
|
||||
case '"':
|
||||
{
|
||||
const char *codes = "\\bfnrt/\"";
|
||||
char *p = (char*) strchr(codes, c);
|
||||
if (pushchar(json, "\\\b\f\n\r\t/\""[p - codes]) != 0)
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
json_error(json, "bad escaped byte, '%c'", c);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
char_needs_escaping(int c)
|
||||
{
|
||||
if ((c >= 0) && (c < 0x20 || c == 0x22 || c == 0x5c)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
utf8_seq_length(char byte)
|
||||
{
|
||||
unsigned char u = (unsigned char) byte;
|
||||
if (u < 0x80) return 1;
|
||||
|
||||
if (0x80 <= u && u <= 0xBF)
|
||||
{
|
||||
// second, third or fourth byte of a multi-byte
|
||||
// sequence, i.e. a "continuation byte"
|
||||
return 0;
|
||||
}
|
||||
else if (u == 0xC0 || u == 0xC1)
|
||||
{
|
||||
// overlong encoding of an ASCII byte
|
||||
return 0;
|
||||
}
|
||||
else if (0xC2 <= u && u <= 0xDF)
|
||||
{
|
||||
// 2-byte sequence
|
||||
return 2;
|
||||
}
|
||||
else if (0xE0 <= u && u <= 0xEF)
|
||||
{
|
||||
// 3-byte sequence
|
||||
return 3;
|
||||
}
|
||||
else if (0xF0 <= u && u <= 0xF4)
|
||||
{
|
||||
// 4-byte sequence
|
||||
return 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// u >= 0xF5
|
||||
// Restricted (start of 4-, 5- or 6-byte sequence) or invalid UTF-8
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
is_legal_utf8(const unsigned char *bytes, int length)
|
||||
{
|
||||
if (0 == bytes || 0 == length) return 0;
|
||||
|
||||
unsigned char a;
|
||||
const unsigned char* srcptr = bytes + length;
|
||||
switch (length)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
// Everything else falls through when true.
|
||||
case 4:
|
||||
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
|
||||
case 3:
|
||||
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
|
||||
case 2:
|
||||
a = (*--srcptr);
|
||||
switch (*bytes)
|
||||
{
|
||||
case 0xE0:
|
||||
if (a < 0xA0 || a > 0xBF) return 0;
|
||||
break;
|
||||
case 0xED:
|
||||
if (a < 0x80 || a > 0x9F) return 0;
|
||||
break;
|
||||
case 0xF0:
|
||||
if (a < 0x90 || a > 0xBF) return 0;
|
||||
break;
|
||||
case 0xF4:
|
||||
if (a < 0x80 || a > 0x8F) return 0;
|
||||
break;
|
||||
default:
|
||||
if (a < 0x80 || a > 0xBF) return 0;
|
||||
}
|
||||
case 1:
|
||||
if (*bytes >= 0x80 && *bytes < 0xC2) return 0;
|
||||
}
|
||||
return *bytes <= 0xF4;
|
||||
}
|
||||
|
||||
static int
|
||||
read_utf8(json_stream* json, int next_char)
|
||||
{
|
||||
int count = utf8_seq_length(next_char);
|
||||
if (!count)
|
||||
{
|
||||
json_error(json, "%s", "Bad character.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buffer[4];
|
||||
buffer[0] = next_char;
|
||||
for (int i = 1; i < count; ++i)
|
||||
{
|
||||
buffer[i] = json->source.get(&json->source);;
|
||||
}
|
||||
|
||||
if (!is_legal_utf8((unsigned char*) buffer, count))
|
||||
{
|
||||
json_error(json, "%s", "No legal UTF8 found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (pushchar(json, buffer[i]) != 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum json_type
|
||||
read_string(json_stream *json)
|
||||
{
|
||||
if (init_string(json) != 0)
|
||||
return JSON_ERROR;
|
||||
while (1) {
|
||||
int c = json->source.get(&json->source);
|
||||
if (c == EOF) {
|
||||
json_error(json, "%s", "unterminated string literal");
|
||||
return JSON_ERROR;
|
||||
} else if (c == '"') {
|
||||
if (pushchar(json, '\0') == 0)
|
||||
return JSON_STRING;
|
||||
else
|
||||
return JSON_ERROR;
|
||||
} else if (c == '\\') {
|
||||
if (read_escaped(json) != 0)
|
||||
return JSON_ERROR;
|
||||
} else if ((unsigned) c >= 0x80) {
|
||||
if (read_utf8(json, c) != 0)
|
||||
return JSON_ERROR;
|
||||
} else {
|
||||
if (char_needs_escaping(c)) {
|
||||
json_error(json, "%s", "unescaped control character in string");
|
||||
return JSON_ERROR;
|
||||
}
|
||||
|
||||
if (pushchar(json, c) != 0)
|
||||
return JSON_ERROR;
|
||||
}
|
||||
}
|
||||
return JSON_ERROR;
|
||||
}
|
||||
|
||||
static int
|
||||
is_digit(int c)
|
||||
{
|
||||
return c >= 48 /*0*/ && c <= 57 /*9*/;
|
||||
}
|
||||
|
||||
static int
|
||||
read_digits(json_stream *json)
|
||||
{
|
||||
unsigned nread = 0;
|
||||
while (is_digit(json->source.peek(&json->source))) {
|
||||
if (pushchar(json, json->source.get(&json->source)) != 0)
|
||||
return -1;
|
||||
|
||||
nread++;
|
||||
}
|
||||
|
||||
if (nread == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum json_type
|
||||
read_number(json_stream *json, int c)
|
||||
{
|
||||
if (pushchar(json, c) != 0)
|
||||
return JSON_ERROR;
|
||||
if (c == '-') {
|
||||
c = json->source.get(&json->source);
|
||||
if (is_digit(c)) {
|
||||
return read_number(json, c);
|
||||
} else {
|
||||
json_error(json, "unexpected byte, '%c'", c);
|
||||
}
|
||||
} else if (strchr("123456789", c) != NULL) {
|
||||
c = json->source.peek(&json->source);
|
||||
if (is_digit(c)) {
|
||||
if (read_digits(json) != 0)
|
||||
return JSON_ERROR;
|
||||
}
|
||||
}
|
||||
/* Up to decimal or exponent has been read. */
|
||||
c = json->source.peek(&json->source);
|
||||
if (strchr(".eE", c) == NULL) {
|
||||
if (pushchar(json, '\0') != 0)
|
||||
return JSON_ERROR;
|
||||
else
|
||||
return JSON_NUMBER;
|
||||
}
|
||||
if (c == '.') {
|
||||
json->source.get(&json->source); // consume .
|
||||
if (pushchar(json, c) != 0)
|
||||
return JSON_ERROR;
|
||||
if (read_digits(json) != 0)
|
||||
return JSON_ERROR;
|
||||
}
|
||||
/* Check for exponent. */
|
||||
c = json->source.peek(&json->source);
|
||||
if (c == 'e' || c == 'E') {
|
||||
json->source.get(&json->source); // consume e/E
|
||||
if (pushchar(json, c) != 0)
|
||||
return JSON_ERROR;
|
||||
c = json->source.peek(&json->source);
|
||||
if (c == '+' || c == '-') {
|
||||
json->source.get(&json->source); // consume
|
||||
if (pushchar(json, c) != 0)
|
||||
return JSON_ERROR;
|
||||
if (read_digits(json) != 0)
|
||||
return JSON_ERROR;
|
||||
} else if (is_digit(c)) {
|
||||
if (read_digits(json) != 0)
|
||||
return JSON_ERROR;
|
||||
} else {
|
||||
json_error(json, "unexpected byte in number, '%c'", c);
|
||||
return JSON_ERROR;
|
||||
}
|
||||
}
|
||||
if (pushchar(json, '\0') != 0)
|
||||
return JSON_ERROR;
|
||||
else
|
||||
return JSON_NUMBER;
|
||||
}
|
||||
|
||||
static int
|
||||
json_isspace(int c)
|
||||
{
|
||||
switch (c) {
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0d:
|
||||
case 0x20:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the next non-whitespace character in the stream. */
|
||||
static int next(json_stream *json)
|
||||
{
|
||||
int c;
|
||||
while (json_isspace(c = json->source.get(&json->source)))
|
||||
if (c == '\n')
|
||||
json->lineno++;
|
||||
return c;
|
||||
}
|
||||
|
||||
static enum json_type
|
||||
read_value(json_stream *json, int c)
|
||||
{
|
||||
json->ntokens++;
|
||||
switch (c) {
|
||||
case EOF:
|
||||
json_error(json, "%s", "unexpected end of data");
|
||||
return JSON_ERROR;
|
||||
case '{':
|
||||
return push(json, JSON_OBJECT);
|
||||
case '[':
|
||||
return push(json, JSON_ARRAY);
|
||||
case '"':
|
||||
return read_string(json);
|
||||
case 'n':
|
||||
return is_match(json, "ull", JSON_NULL);
|
||||
case 'f':
|
||||
return is_match(json, "alse", JSON_FALSE);
|
||||
case 't':
|
||||
return is_match(json, "rue", JSON_TRUE);
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
if (init_string(json) != 0)
|
||||
return JSON_ERROR;
|
||||
return read_number(json, c);
|
||||
default:
|
||||
json_error(json, "unexpected byte, '%c'", c);
|
||||
return JSON_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
enum json_type json_peek(json_stream *json)
|
||||
{
|
||||
enum json_type next = json_next(json);
|
||||
json->next = next;
|
||||
return next;
|
||||
}
|
||||
|
||||
enum json_type json_next(json_stream *json)
|
||||
{
|
||||
if (json->flags & JSON_FLAG_ERROR)
|
||||
return JSON_ERROR;
|
||||
if (json->next != 0) {
|
||||
enum json_type next = json->next;
|
||||
json->next = (enum json_type) 0;
|
||||
return next;
|
||||
}
|
||||
if (json->ntokens > 0 && json->stack_top == (size_t)-1) {
|
||||
int c;
|
||||
|
||||
do {
|
||||
c = json->source.peek(&json->source);
|
||||
if (json_isspace(c)) {
|
||||
c = json->source.get(&json->source);
|
||||
}
|
||||
} while (json_isspace(c));
|
||||
|
||||
if (!(json->flags & JSON_FLAG_STREAMING) && c != EOF) {
|
||||
return JSON_ERROR;
|
||||
}
|
||||
|
||||
return JSON_DONE;
|
||||
}
|
||||
int c = next(json);
|
||||
if (json->stack_top == (size_t)-1)
|
||||
return read_value(json, c);
|
||||
if (json->stack[json->stack_top].type == JSON_ARRAY) {
|
||||
if (json->stack[json->stack_top].count == 0) {
|
||||
if (c == ']') {
|
||||
return pop(json, c, JSON_ARRAY);
|
||||
}
|
||||
json->stack[json->stack_top].count++;
|
||||
return read_value(json, c);
|
||||
} else if (c == ',') {
|
||||
json->stack[json->stack_top].count++;
|
||||
return read_value(json, next(json));
|
||||
} else if (c == ']') {
|
||||
return pop(json, c, JSON_ARRAY);
|
||||
} else {
|
||||
json_error(json, "unexpected byte, '%c'", c);
|
||||
return JSON_ERROR;
|
||||
}
|
||||
} else if (json->stack[json->stack_top].type == JSON_OBJECT) {
|
||||
if (json->stack[json->stack_top].count == 0) {
|
||||
if (c == '}') {
|
||||
return pop(json, c, JSON_OBJECT);
|
||||
}
|
||||
|
||||
/* No property value pairs yet. */
|
||||
enum json_type value = read_value(json, c);
|
||||
if (value != JSON_STRING) {
|
||||
json_error(json, "%s", "expected property name or '}'");
|
||||
return JSON_ERROR;
|
||||
} else {
|
||||
json->stack[json->stack_top].count++;
|
||||
return value;
|
||||
}
|
||||
} else if ((json->stack[json->stack_top].count % 2) == 0) {
|
||||
/* Expecting comma followed by property name. */
|
||||
if (c != ',' && c != '}') {
|
||||
json_error(json, "%s", "expected ',' or '}'");
|
||||
return JSON_ERROR;
|
||||
} else if (c == '}') {
|
||||
return pop(json, c, JSON_OBJECT);
|
||||
} else {
|
||||
enum json_type value = read_value(json, next(json));
|
||||
if (value != JSON_STRING) {
|
||||
json_error(json, "%s", "expected property name");
|
||||
return JSON_ERROR;
|
||||
} else {
|
||||
json->stack[json->stack_top].count++;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} else if ((json->stack[json->stack_top].count % 2) == 1) {
|
||||
/* Expecting colon followed by value. */
|
||||
if (c != ':') {
|
||||
json_error(json, "%s", "expected ':' after property name");
|
||||
return JSON_ERROR;
|
||||
} else {
|
||||
json->stack[json->stack_top].count++;
|
||||
return read_value(json, next(json));
|
||||
}
|
||||
}
|
||||
}
|
||||
json_error(json, "%s", "invalid parser state");
|
||||
return JSON_ERROR;
|
||||
}
|
||||
|
||||
void json_reset(json_stream *json)
|
||||
{
|
||||
json->stack_top = -1;
|
||||
json->ntokens = 0;
|
||||
json->flags &= ~JSON_FLAG_ERROR;
|
||||
json->errmsg[0] = '\0';
|
||||
}
|
||||
|
||||
const char *json_get_string(json_stream *json, size_t *length)
|
||||
{
|
||||
if (length != NULL)
|
||||
*length = json->data.string_fill;
|
||||
if (json->data.string == NULL)
|
||||
return "";
|
||||
else
|
||||
return json->data.string;
|
||||
}
|
||||
|
||||
double json_get_number(json_stream *json)
|
||||
{
|
||||
char *p = json->data.string;
|
||||
return p == NULL ? 0 : strtod(p, NULL);
|
||||
}
|
||||
|
||||
const char *json_get_error(json_stream *json)
|
||||
{
|
||||
return json->flags & JSON_FLAG_ERROR ? json->errmsg : NULL;
|
||||
}
|
||||
|
||||
size_t json_get_lineno(json_stream *json)
|
||||
{
|
||||
return json->lineno;
|
||||
}
|
||||
|
||||
size_t json_get_position(json_stream *json)
|
||||
{
|
||||
return json->source.position;
|
||||
}
|
||||
|
||||
size_t json_get_depth(json_stream *json)
|
||||
{
|
||||
return json->stack_top + 1;
|
||||
}
|
||||
|
||||
void json_open_buffer(json_stream *json, const void *buffer, size_t size)
|
||||
{
|
||||
init(json);
|
||||
json->source.get = buffer_get;
|
||||
json->source.peek = buffer_peek;
|
||||
json->source.source.buffer.buffer = (char*) buffer;
|
||||
json->source.source.buffer.length = size;
|
||||
}
|
||||
|
||||
void json_open_string(json_stream *json, const char *string)
|
||||
{
|
||||
json_open_buffer(json, string, strlen(string));
|
||||
}
|
||||
|
||||
void json_open_stream(json_stream *json, FILE * stream)
|
||||
{
|
||||
init(json);
|
||||
json->source.get = stream_get;
|
||||
json->source.peek = stream_peek;
|
||||
json->source.source.stream.stream = stream;
|
||||
}
|
||||
|
||||
static int user_get(struct json_source *json)
|
||||
{
|
||||
return json->source.user.get(json->source.user.ptr);
|
||||
}
|
||||
|
||||
static int user_peek(struct json_source *json)
|
||||
{
|
||||
return json->source.user.peek(json->source.user.ptr);
|
||||
}
|
||||
|
||||
void json_open_user(json_stream *json, json_user_io get, json_user_io peek, void *user)
|
||||
{
|
||||
init(json);
|
||||
json->source.get = user_get;
|
||||
json->source.peek = user_peek;
|
||||
json->source.source.user.ptr = user;
|
||||
json->source.source.user.get = get;
|
||||
json->source.source.user.peek = peek;
|
||||
}
|
||||
|
||||
void json_set_allocator(json_stream *json, json_allocator *a)
|
||||
{
|
||||
json->alloc = *a;
|
||||
}
|
||||
|
||||
void json_set_streaming(json_stream *json, bool streaming)
|
||||
{
|
||||
if (streaming)
|
||||
json->flags |= JSON_FLAG_STREAMING;
|
||||
else
|
||||
json->flags &= ~JSON_FLAG_STREAMING;
|
||||
}
|
||||
|
||||
void json_close(json_stream *json)
|
||||
{
|
||||
json->alloc.free(json->stack);
|
||||
json->alloc.free(json->data.string);
|
||||
}
|
||||
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
#ifndef PDJSON_H
|
||||
#define PDJSON_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if !defined(__cplusplus) && !defined(_MSC_VER) // for poco 1.8.x we must compile as C++
|
||||
#if defined(__STDC_VERSION__) || (__STDC_VERSION__ >= 199901L)
|
||||
#include <stdbool.h>
|
||||
#else
|
||||
#ifndef bool
|
||||
#define bool int
|
||||
#endif
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
#endif // __STDC_VERSION__
|
||||
#endif
|
||||
|
||||
enum json_type {
|
||||
JSON_ERROR = 1, JSON_DONE,
|
||||
JSON_OBJECT, JSON_OBJECT_END, JSON_ARRAY, JSON_ARRAY_END,
|
||||
JSON_STRING, JSON_NUMBER, JSON_TRUE, JSON_FALSE, JSON_NULL
|
||||
};
|
||||
|
||||
struct json_allocator {
|
||||
void *(*malloc)(size_t);
|
||||
void *(*realloc)(void *, size_t);
|
||||
void (*free)(void *);
|
||||
};
|
||||
|
||||
typedef int (*json_user_io) (void *user);
|
||||
|
||||
typedef struct json_stream json_stream;
|
||||
typedef struct json_allocator json_allocator;
|
||||
|
||||
//extern const char *json_typename[];
|
||||
|
||||
void json_open_buffer(json_stream *json, const void *buffer, size_t size);
|
||||
void json_open_string(json_stream *json, const char *string);
|
||||
void json_open_stream(json_stream *json, FILE *stream);
|
||||
void json_open_user(json_stream *json, json_user_io get, json_user_io peek, void *user);
|
||||
void json_close(json_stream *json);
|
||||
|
||||
void json_set_allocator(json_stream *json, json_allocator *a);
|
||||
void json_set_streaming(json_stream *json, bool strict);
|
||||
|
||||
enum json_type json_next(json_stream *json);
|
||||
enum json_type json_peek(json_stream *json);
|
||||
void json_reset(json_stream *json);
|
||||
const char *json_get_string(json_stream *json, size_t *length);
|
||||
double json_get_number(json_stream *json);
|
||||
|
||||
size_t json_get_lineno(json_stream *json);
|
||||
size_t json_get_position(json_stream *json);
|
||||
size_t json_get_depth(json_stream *json);
|
||||
const char *json_get_error(json_stream *json);
|
||||
|
||||
/* internal */
|
||||
|
||||
struct json_source {
|
||||
int (*get) (struct json_source *);
|
||||
int (*peek) (struct json_source *);
|
||||
size_t position;
|
||||
union {
|
||||
struct {
|
||||
FILE *stream;
|
||||
} stream;
|
||||
struct {
|
||||
const char *buffer;
|
||||
size_t length;
|
||||
} buffer;
|
||||
struct {
|
||||
void *ptr;
|
||||
json_user_io get;
|
||||
json_user_io peek;
|
||||
} user;
|
||||
} source;
|
||||
};
|
||||
|
||||
struct json_stream {
|
||||
size_t lineno;
|
||||
|
||||
struct json_stack *stack;
|
||||
size_t stack_top;
|
||||
size_t stack_size;
|
||||
enum json_type next;
|
||||
unsigned flags;
|
||||
|
||||
struct {
|
||||
char *string;
|
||||
size_t string_fill;
|
||||
size_t string_size;
|
||||
} data;
|
||||
|
||||
size_t ntokens;
|
||||
|
||||
struct json_source source;
|
||||
struct json_allocator alloc;
|
||||
char errmsg[128];
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user