1911 lines
74 KiB
C
Raw Normal View History

2015-05-01 17:59:31 +08:00
// Tencent is pleased to support the open source community by making RapidJSON available->
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License-> You may obtain a copy of the License at
//
// http://opensource->org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
// specific language governing permissions and limitations under the License->
#ifndef RAPIDJSON_SCHEMA_H_
#define RAPIDJSON_SCHEMA_H_
#include "document.h"
#include "pointer.h"
2015-05-07 16:42:41 +08:00
#include <cmath> // HUGE_VAL, abs, floor
2015-05-01 17:59:31 +08:00
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(weak-vtables)
RAPIDJSON_DIAG_OFF(exit-time-destructors)
2016-01-27 14:22:05 +08:00
RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
#endif
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
#else
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
#endif
#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
2015-05-05 00:08:36 +08:00
#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
2015-05-08 13:44:52 +08:00
#else
#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
2015-05-05 00:08:36 +08:00
#endif
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
#include "internal/regex.h"
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
2015-05-05 00:08:36 +08:00
#include <regex>
#endif
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
#define RAPIDJSON_SCHEMA_HAS_REGEX 1
#else
#define RAPIDJSON_SCHEMA_HAS_REGEX 0
#endif
#ifndef RAPIDJSON_SCHEMA_VERBOSE
#define RAPIDJSON_SCHEMA_VERBOSE 0
#endif
#if RAPIDJSON_SCHEMA_VERBOSE
#include "stringbuffer.h"
#endif
2015-05-01 22:38:00 +08:00
#if defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(variadic-macros)
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Verbose Utilities
#if RAPIDJSON_SCHEMA_VERBOSE
namespace internal {
inline void PrintInvalidKeyword(const char* keyword) {
printf("Fail keyword: %s\n", keyword);
}
inline void PrintInvalidKeyword(const wchar_t* keyword) {
wprintf(L"Fail keyword: %ls\n", keyword);
}
inline void PrintInvalidDocument(const char* document) {
printf("Fail document: %s\n\n", document);
}
inline void PrintInvalidDocument(const wchar_t* document) {
wprintf(L"Fail document: %ls\n\n", document);
}
inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
}
inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
}
} // namespace internal
#endif // RAPIDJSON_SCHEMA_VERBOSE
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_INVALID_KEYWORD_RETURN
#if RAPIDJSON_SCHEMA_VERBOSE
#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
#else
#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
#endif
2015-05-11 23:53:03 +08:00
#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
2015-05-16 15:51:36 +08:00
RAPIDJSON_MULTILINEMACRO_BEGIN\
context.invalidKeyword = keyword.GetString();\
RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
2015-05-11 23:53:03 +08:00
return false;\
2015-05-16 15:51:36 +08:00
RAPIDJSON_MULTILINEMACRO_END
2015-05-11 23:53:03 +08:00
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// Forward declarations
2015-05-02 17:46:55 +08:00
2015-05-09 15:58:01 +08:00
template <typename ValueType, typename Allocator>
class GenericSchemaDocument;
namespace internal {
2015-05-01 17:59:31 +08:00
2015-05-09 15:58:01 +08:00
template <typename SchemaDocumentType>
class Schema;
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// ISchemaValidator
2015-05-05 16:44:05 +08:00
class ISchemaValidator {
public:
virtual ~ISchemaValidator() {}
virtual bool IsValid() const = 0;
};
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// ISchemaStateFactory
2015-05-09 14:22:04 +08:00
template <typename SchemaType>
class ISchemaStateFactory {
public:
virtual ~ISchemaStateFactory() {}
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
2015-05-17 20:43:52 +08:00
virtual void* CreateHasher() = 0;
virtual uint64_t GetHashCode(void* hasher) = 0;
2015-05-17 20:43:52 +08:00
virtual void DestroryHasher(void* hasher) = 0;
virtual void* MallocState(size_t size) = 0;
virtual void* ReallocState(void* originalPtr, size_t originalSize, size_t newSize) = 0;
virtual void FreeState(void* p) = 0;
};
2015-05-10 08:59:58 +08:00
///////////////////////////////////////////////////////////////////////////////
// Hasher
// For comparison of compound value
template<typename Encoding, typename Allocator>
2015-05-10 08:59:58 +08:00
class Hasher {
public:
typedef typename Encoding::Ch Ch;
2015-05-10 08:59:58 +08:00
Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
2015-05-10 08:59:58 +08:00
bool Null() { return WriteType(kNullType); }
bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
bool Double(double d) {
Number n;
if (d < 0) n.u.i = static_cast<int64_t>(d);
else n.u.u = static_cast<uint64_t>(d);
n.d = d;
return WriteNumber(n);
}
2015-05-10 10:26:33 +08:00
2015-05-10 08:59:58 +08:00
bool String(const Ch* str, SizeType len, bool) {
WriteBuffer(kStringType, str, len * sizeof(Ch));
return true;
}
2015-05-10 10:26:33 +08:00
2015-05-10 08:59:58 +08:00
bool StartObject() { return true; }
bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
bool EndObject(SizeType memberCount) {
uint64_t h = Hash(0, kObjectType);
uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
for (SizeType i = 0; i < memberCount; i++)
h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
*stack_.template Push<uint64_t>() = h;
return true;
}
2015-05-10 10:26:33 +08:00
2015-05-10 08:59:58 +08:00
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) {
uint64_t h = Hash(0, kArrayType);
uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
for (SizeType i = 0; i < elementCount; i++)
h = Hash(h, e[i]); // Use hash to achieve element order sensitive
*stack_.template Push<uint64_t>() = h;
return true;
}
2015-05-10 10:26:33 +08:00
bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
2015-05-10 08:59:58 +08:00
uint64_t GetHashCode() const {
2015-05-10 10:26:33 +08:00
RAPIDJSON_ASSERT(IsValid());
2015-05-10 08:59:58 +08:00
return *stack_.template Top<uint64_t>();
}
private:
static const size_t kDefaultSize = 256;
struct Number {
union U {
uint64_t u;
int64_t i;
}u;
double d;
};
2015-05-10 10:26:33 +08:00
bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
bool WriteBuffer(Type type, const void* data, size_t len) {
2015-05-10 08:59:58 +08:00
// FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
const unsigned char* d = static_cast<const unsigned char*>(data);
for (size_t i = 0; i < len; i++)
h = Hash(h, d[i]);
*stack_.template Push<uint64_t>() = h;
return true;
}
static uint64_t Hash(uint64_t h, uint64_t d) {
static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
h ^= d;
h *= kPrime;
return h;
}
Stack<Allocator> stack_;
};
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// SchemaValidationContext
2015-05-08 13:37:30 +08:00
2015-05-09 15:58:01 +08:00
template <typename SchemaDocumentType>
2015-05-01 17:59:31 +08:00
struct SchemaValidationContext {
2015-05-09 15:58:01 +08:00
typedef Schema<SchemaDocumentType> SchemaType;
typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
2015-05-10 17:44:43 +08:00
typedef typename SchemaType::ValueType ValueType;
typedef typename ValueType::Ch Ch;
2015-05-09 14:22:04 +08:00
enum PatternValidatorType {
kPatternValidatorOnly,
kPatternValidatorWithProperty,
kPatternValidatorWithAdditionalProperty
};
SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
factory(f),
2015-05-08 14:03:05 +08:00
schema(s),
valueSchema(),
2015-05-11 23:53:03 +08:00
invalidKeyword(),
2015-05-10 12:17:23 +08:00
hasher(),
2015-05-17 20:43:52 +08:00
arrayElementHashCodes(),
validators(),
validatorCount(),
patternPropertiesValidators(),
patternPropertiesValidatorCount(),
2015-05-08 13:37:30 +08:00
patternPropertiesSchemas(),
patternPropertiesSchemaCount(),
valuePatternValidatorType(kPatternValidatorOnly),
2015-05-08 14:03:05 +08:00
objectDependencies(),
2015-05-10 17:44:43 +08:00
inArray(false),
valueUniqueness(false),
arrayUniqueness(false)
2015-05-05 16:44:05 +08:00
{
}
2015-05-01 17:59:31 +08:00
~SchemaValidationContext() {
2015-05-17 20:43:52 +08:00
if (hasher)
factory.DestroryHasher(hasher);
if (validators) {
for (SizeType i = 0; i < validatorCount; i++)
factory.DestroySchemaValidator(validators[i]);
factory.FreeState(validators);
}
if (patternPropertiesValidators) {
for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
factory.DestroySchemaValidator(patternPropertiesValidators[i]);
factory.FreeState(patternPropertiesValidators);
}
2016-01-30 22:41:09 +08:00
if (patternPropertiesSchemas)
factory.FreeState(patternPropertiesSchemas);
if (objectDependencies)
factory.FreeState(objectDependencies);
}
2015-05-01 17:59:31 +08:00
SchemaValidatorFactoryType& factory;
const SchemaType* schema;
const SchemaType* valueSchema;
const Ch* invalidKeyword;
2016-01-30 22:41:09 +08:00
void* hasher; // Only validator access
2015-05-17 20:43:52 +08:00
void* arrayElementHashCodes; // Only validator access this
ISchemaValidator** validators;
SizeType validatorCount;
ISchemaValidator** patternPropertiesValidators;
SizeType patternPropertiesValidatorCount;
const SchemaType** patternPropertiesSchemas;
2015-05-08 13:37:30 +08:00
SizeType patternPropertiesSchemaCount;
PatternValidatorType valuePatternValidatorType;
PatternValidatorType objectPatternValidatorType;
2015-05-01 17:59:31 +08:00
SizeType objectRequiredCount;
SizeType arrayElementIndex;
bool* objectDependencies;
2015-05-07 00:59:51 +08:00
bool inArray;
2015-05-10 17:44:43 +08:00
bool valueUniqueness;
bool arrayUniqueness;
2015-05-01 17:59:31 +08:00
};
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// Schema
2015-05-09 15:58:01 +08:00
template <typename SchemaDocumentType>
2015-05-08 16:26:36 +08:00
class Schema {
2015-05-01 17:59:31 +08:00
public:
2015-05-09 15:58:01 +08:00
typedef typename SchemaDocumentType::ValueType ValueType;
typedef typename SchemaDocumentType::AllocatorType AllocatorType;
typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename ValueType::EncodingType EncodingType;
typedef typename EncodingType::Ch Ch;
typedef SchemaValidationContext<SchemaDocumentType> Context;
typedef Schema<SchemaDocumentType> SchemaType;
2015-05-11 00:37:18 +08:00
typedef GenericValue<EncodingType, AllocatorType> SValue;
2015-05-09 22:49:36 +08:00
friend class GenericSchemaDocument<ValueType, AllocatorType>;
2015-05-09 15:58:01 +08:00
2015-05-16 10:25:10 +08:00
Schema(SchemaDocumentType* document, const PointerType& p, const ValueType& value, AllocatorType* allocator) :
2015-05-11 00:37:18 +08:00
allocator_(allocator),
2015-05-10 12:17:23 +08:00
enum_(),
enumCount_(),
2015-05-07 00:59:51 +08:00
not_(),
2015-05-07 15:05:55 +08:00
type_((1 << kTotalSchemaType) - 1), // typeless
validatorCount_(),
2015-05-07 00:59:51 +08:00
properties_(),
additionalPropertiesSchema_(),
2015-05-07 00:59:51 +08:00
patternProperties_(),
patternPropertyCount_(),
propertyCount_(),
requiredCount_(),
minProperties_(),
maxProperties_(SizeType(~0)),
additionalProperties_(true),
2015-05-07 00:59:51 +08:00
hasDependencies_(),
hasSchemaDependencies_(),
additionalItemsSchema_(),
2015-05-07 00:59:51 +08:00
itemsList_(),
itemsTuple_(),
itemsTupleCount_(),
minItems_(),
maxItems_(SizeType(~0)),
additionalItems_(true),
2015-05-10 17:44:43 +08:00
uniqueItems_(false),
2015-05-07 00:59:51 +08:00
pattern_(),
minLength_(0),
maxLength_(~SizeType(0)),
exclusiveMinimum_(false),
exclusiveMaximum_(false)
{
2015-05-09 15:58:01 +08:00
typedef typename SchemaDocumentType::ValueType ValueType;
2015-05-07 15:05:55 +08:00
typedef typename ValueType::ConstValueIterator ConstValueIterator;
typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
if (!value.IsObject())
return;
if (const ValueType* v = GetMember(value, GetTypeString())) {
2015-05-07 15:05:55 +08:00
type_ = 0;
if (v->IsString())
AddType(*v);
else if (v->IsArray())
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
2015-05-07 00:59:51 +08:00
AddType(*itr);
}
if (const ValueType* v = GetMember(value, GetEnumString()))
2015-05-10 12:17:23 +08:00
if (v->IsArray() && v->Size() > 0) {
2015-05-11 00:37:18 +08:00
enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
2015-05-10 12:17:23 +08:00
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
char buffer[256 + 24];
MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
EnumHasherType h(&hasherAllocator, 256);
2015-05-10 12:17:23 +08:00
itr->Accept(h);
enum_[enumCount_++] = h.GetHashCode();
}
}
2015-05-05 16:44:05 +08:00
AssignIfExist(allOf_, document, p, value, GetAllOfString());
AssignIfExist(anyOf_, document, p, value, GetAnyOfString());
AssignIfExist(oneOf_, document, p, value, GetOneOfString());
2015-05-01 20:16:41 +08:00
if (const ValueType* v = GetMember(value, GetNotString())) {
document->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v);
notValidatorIndex_ = validatorCount_;
validatorCount_++;
}
2015-05-09 11:46:45 +08:00
2015-05-07 00:59:51 +08:00
// Object
2015-05-07 23:05:05 +08:00
const ValueType* properties = GetMember(value, GetPropertiesString());
const ValueType* required = GetMember(value, GetRequiredString());
const ValueType* dependencies = GetMember(value, GetDependenciesString());
2015-05-07 23:05:05 +08:00
{
// Gather properties from properties/required/dependencies
SValue allProperties(kArrayType);
if (properties && properties->IsObject())
for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
AddUniqueElement(allProperties, itr->name);
if (required && required->IsArray())
for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
if (itr->IsString())
AddUniqueElement(allProperties, *itr);
if (dependencies && dependencies->IsObject())
for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
AddUniqueElement(allProperties, itr->name);
if (itr->value.IsArray())
for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
if (i->IsString())
AddUniqueElement(allProperties, *i);
}
if (allProperties.Size() > 0) {
propertyCount_ = allProperties.Size();
2015-05-11 00:37:18 +08:00
properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
2015-05-07 23:05:05 +08:00
for (SizeType i = 0; i < propertyCount_; i++) {
2015-05-11 00:37:18 +08:00
new (&properties_[i]) Property();
2015-05-07 23:37:05 +08:00
properties_[i].name = allProperties[i];
properties_[i].schema = GetTypeless();
2015-05-07 15:05:55 +08:00
}
2015-05-01 17:59:31 +08:00
}
2015-05-07 23:05:05 +08:00
}
2015-05-09 08:37:49 +08:00
if (properties && properties->IsObject()) {
PointerType q = p.Append(GetPropertiesString(), allocator_);
2015-05-07 23:05:05 +08:00
for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
SizeType index;
if (FindPropertyIndex(itr->name, &index))
document->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value);
2015-05-07 23:05:05 +08:00
}
2015-05-09 08:37:49 +08:00
}
2015-05-01 17:59:31 +08:00
if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
2015-05-11 00:37:18 +08:00
patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
2015-05-05 00:47:06 +08:00
patternPropertyCount_ = 0;
2015-05-07 15:05:55 +08:00
for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
2015-05-11 00:37:18 +08:00
new (&patternProperties_[patternPropertyCount_]) PatternProperty();
2015-05-07 15:05:55 +08:00
patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
document->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value);
2015-05-05 00:47:06 +08:00
patternPropertyCount_++;
}
}
2015-05-07 23:05:05 +08:00
if (required && required->IsArray())
for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
if (itr->IsString()) {
SizeType index;
if (FindPropertyIndex(*itr, &index)) {
properties_[index].required = true;
requiredCount_++;
2015-05-01 17:59:31 +08:00
}
2015-05-07 23:05:05 +08:00
}
2015-05-01 17:59:31 +08:00
2015-05-07 23:05:05 +08:00
if (dependencies && dependencies->IsObject()) {
PointerType q = p.Append(GetDependenciesString(), allocator_);
2015-05-07 23:05:05 +08:00
hasDependencies_ = true;
for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
SizeType sourceIndex;
if (FindPropertyIndex(itr->name, &sourceIndex)) {
if (itr->value.IsArray()) {
2015-05-11 00:37:18 +08:00
properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
2015-05-07 23:05:05 +08:00
std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
SizeType targetIndex;
if (FindPropertyIndex(*targetItr, &targetIndex))
properties_[sourceIndex].dependencies[targetIndex] = true;
}
}
2015-05-07 23:05:05 +08:00
else if (itr->value.IsObject()) {
hasSchemaDependencies_ = true;
document->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value);
properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
validatorCount_++;
2015-05-07 23:05:05 +08:00
}
2015-05-01 17:59:31 +08:00
}
}
2015-05-07 23:05:05 +08:00
}
2015-05-01 17:59:31 +08:00
if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
2015-05-07 15:05:55 +08:00
if (v->IsBool())
additionalProperties_ = v->GetBool();
2015-05-07 15:05:55 +08:00
else if (v->IsObject())
document->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v);
2015-05-01 17:59:31 +08:00
}
AssignIfExist(minProperties_, value, GetMinPropertiesString());
AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
// Array
if (const ValueType* v = GetMember(value, GetItemsString())) {
PointerType q = p.Append(GetItemsString(), allocator_);
2015-05-07 15:05:55 +08:00
if (v->IsObject()) // List validation
2015-05-16 15:51:36 +08:00
document->CreateSchema(&itemsList_, q, *v);
2015-05-07 15:05:55 +08:00
else if (v->IsArray()) { // Tuple validation
2015-05-11 00:37:18 +08:00
itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
2015-05-09 08:37:49 +08:00
SizeType index = 0;
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
document->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr);
2015-05-07 00:59:51 +08:00
}
}
2015-05-02 17:46:55 +08:00
AssignIfExist(minItems_, value, GetMinItemsString());
AssignIfExist(maxItems_, value, GetMaxItemsString());
if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
if (v->IsBool())
additionalItems_ = v->GetBool();
else if (v->IsObject())
document->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v);
}
2015-05-05 16:44:05 +08:00
AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
2015-05-10 17:44:43 +08:00
2015-05-07 00:59:51 +08:00
// String
AssignIfExist(minLength_, value, GetMinLengthString());
AssignIfExist(maxLength_, value, GetMaxLengthString());
2015-05-01 17:59:31 +08:00
if (const ValueType* v = GetMember(value, GetPatternString()))
2015-05-07 15:05:55 +08:00
pattern_ = CreatePattern(*v);
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
// Number
if (const ValueType* v = GetMember(value, GetMinimumString()))
2015-05-13 09:44:25 +08:00
if (v->IsNumber())
minimum_.CopyFrom(*v, *allocator_);
2015-05-01 17:59:31 +08:00
if (const ValueType* v = GetMember(value, GetMaximumString()))
2015-05-13 09:44:25 +08:00
if (v->IsNumber())
maximum_.CopyFrom(*v, *allocator_);
2015-05-01 17:59:31 +08:00
AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
2015-05-01 17:59:31 +08:00
if (const ValueType* v = GetMember(value, GetMultipleOfString()))
2015-05-13 09:44:25 +08:00
if (v->IsNumber() && v->GetDouble() > 0.0)
multipleOf_.CopyFrom(*v, *allocator_);
2015-05-01 17:59:31 +08:00
}
2015-05-08 16:26:36 +08:00
~Schema() {
2015-05-11 00:37:18 +08:00
allocator_->Free(enum_);
if (properties_) {
for (SizeType i = 0; i < propertyCount_; i++)
properties_[i].~Property();
AllocatorType::Free(properties_);
}
if (patternProperties_) {
for (SizeType i = 0; i < patternPropertyCount_; i++)
patternProperties_[i].~PatternProperty();
AllocatorType::Free(patternProperties_);
}
AllocatorType::Free(itemsTuple_);
#if RAPIDJSON_SCHEMA_HAS_REGEX
2015-05-11 00:37:18 +08:00
if (pattern_) {
pattern_->~RegexType();
allocator_->Free(pattern_);
}
2015-05-07 00:59:51 +08:00
#endif
}
2015-05-02 17:46:55 +08:00
2015-05-07 00:59:51 +08:00
bool BeginValue(Context& context) const {
if (context.inArray) {
2015-05-10 17:44:43 +08:00
if (uniqueItems_)
context.valueUniqueness = true;
2015-05-07 00:59:51 +08:00
if (itemsList_)
context.valueSchema = itemsList_;
else if (itemsTuple_) {
if (context.arrayElementIndex < itemsTupleCount_)
context.valueSchema = itemsTuple_[context.arrayElementIndex];
else if (additionalItemsSchema_)
context.valueSchema = additionalItemsSchema_;
2015-05-07 00:59:51 +08:00
else if (additionalItems_)
context.valueSchema = GetTypeless();
else
RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
2015-05-07 00:59:51 +08:00
}
2015-05-02 10:06:48 +08:00
else
2015-05-07 00:59:51 +08:00
context.valueSchema = GetTypeless();
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
context.arrayElementIndex++;
}
2015-05-02 10:06:48 +08:00
return true;
2015-05-01 17:59:31 +08:00
}
2016-01-30 22:41:09 +08:00
RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
if (context.patternPropertiesValidatorCount > 0) {
2015-05-08 13:37:30 +08:00
bool otherValid = false;
SizeType count = context.patternPropertiesValidatorCount;
2015-05-09 14:22:04 +08:00
if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
otherValid = context.patternPropertiesValidators[--count]->IsValid();
2015-05-08 13:37:30 +08:00
bool patternValid = true;
for (SizeType i = 0; i < count; i++)
if (!context.patternPropertiesValidators[i]->IsValid()) {
2015-05-08 13:37:30 +08:00
patternValid = false;
break;
}
2015-05-09 14:22:04 +08:00
if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
2015-05-08 13:37:30 +08:00
if (!patternValid)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
2015-05-08 15:07:31 +08:00
}
2015-05-09 14:22:04 +08:00
else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
2015-05-08 13:37:30 +08:00
if (!patternValid || !otherValid)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
2015-05-08 13:37:30 +08:00
}
2015-05-08 15:07:31 +08:00
else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
2015-05-08 13:37:30 +08:00
}
2015-05-10 12:17:23 +08:00
if (enum_) {
const uint64_t h = context.factory.GetHashCode(context.hasher);
2015-05-10 12:17:23 +08:00
for (SizeType i = 0; i < enumCount_; i++)
if (enum_[i] == h)
goto foundEnum;
RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
2015-05-10 12:17:23 +08:00
foundEnum:;
}
2015-05-07 15:05:55 +08:00
if (allOf_.schemas)
for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
if (!context.validators[i]->IsValid())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
2015-05-07 15:05:55 +08:00
2015-05-07 00:59:51 +08:00
if (anyOf_.schemas) {
for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
if (context.validators[i]->IsValid())
2015-05-07 15:05:55 +08:00
goto foundAny;
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
2015-05-07 15:05:55 +08:00
foundAny:;
2015-05-07 00:59:51 +08:00
}
2015-05-07 15:05:55 +08:00
2015-05-07 00:59:51 +08:00
if (oneOf_.schemas) {
bool oneValid = false;
for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
if (context.validators[i]->IsValid()) {
2015-05-07 00:59:51 +08:00
if (oneValid)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
2015-05-07 00:59:51 +08:00
else
oneValid = true;
}
if (!oneValid)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
2015-05-07 00:59:51 +08:00
}
2015-05-07 15:05:55 +08:00
if (not_ && context.validators[notValidatorIndex_]->IsValid())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
2015-05-11 23:53:03 +08:00
return true;
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool Null(Context& context) const {
2015-05-11 23:53:03 +08:00
if (!(type_ & (1 << kNullSchemaType)))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-11 23:53:03 +08:00
return CreateParallelValidator(context);
2015-05-07 00:59:51 +08:00
}
2015-05-10 12:17:23 +08:00
bool Bool(Context& context, bool) const {
2015-05-11 23:53:03 +08:00
if (!(type_ & (1 << kBooleanSchemaType)))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-11 23:53:03 +08:00
return CreateParallelValidator(context);
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool Int(Context& context, int i) const {
2015-05-13 09:44:25 +08:00
if (!CheckInt(context, i))
return false;
return CreateParallelValidator(context);
2015-05-07 00:59:51 +08:00
}
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
bool Uint(Context& context, unsigned u) const {
2015-05-13 09:44:25 +08:00
if (!CheckUint(context, u))
return false;
return CreateParallelValidator(context);
2015-05-05 00:08:36 +08:00
}
2015-05-07 00:59:51 +08:00
bool Int64(Context& context, int64_t i) const {
2015-05-13 09:44:25 +08:00
if (!CheckInt(context, i))
return false;
return CreateParallelValidator(context);
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool Uint64(Context& context, uint64_t u) const {
2015-05-13 09:44:25 +08:00
if (!CheckUint(context, u))
return false;
return CreateParallelValidator(context);
2015-05-07 00:59:51 +08:00
}
2015-05-02 17:46:55 +08:00
2015-05-07 00:59:51 +08:00
bool Double(Context& context, double d) const {
2015-05-11 23:53:03 +08:00
if (!(type_ & (1 << kNumberSchemaType)))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-13 09:44:25 +08:00
if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
return false;
if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
return false;
if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
return false;
return CreateParallelValidator(context);
2015-05-07 00:59:51 +08:00
}
bool String(Context& context, const Ch* str, SizeType length, bool) const {
2015-05-11 23:53:03 +08:00
if (!(type_ & (1 << kStringSchemaType)))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-05 16:44:05 +08:00
if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
SizeType count;
2015-05-11 23:53:03 +08:00
if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
if (count < minLength_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
2015-05-11 23:53:03 +08:00
if (count > maxLength_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
2015-05-11 23:53:03 +08:00
}
}
2015-05-05 00:08:36 +08:00
2015-05-11 23:53:03 +08:00
if (pattern_ && !IsPatternMatch(pattern_, str, length))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
2015-05-11 23:53:03 +08:00
return CreateParallelValidator(context);
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool StartObject(Context& context) const {
2015-05-11 23:53:03 +08:00
if (!(type_ & (1 << kObjectSchemaType)))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
context.objectRequiredCount = 0;
if (hasDependencies_) {
context.objectDependencies = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
2015-05-07 00:59:51 +08:00
std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_);
2015-05-01 17:59:31 +08:00
}
2015-05-08 13:37:30 +08:00
if (patternProperties_) { // pre-allocate schema array
SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
2015-05-08 13:37:30 +08:00
context.patternPropertiesSchemaCount = 0;
std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
2015-05-08 13:37:30 +08:00
}
2015-05-11 23:53:03 +08:00
return CreateParallelValidator(context);
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool Key(Context& context, const Ch* str, SizeType len, bool) const {
2015-05-08 13:37:30 +08:00
if (patternProperties_) {
context.patternPropertiesSchemaCount = 0;
for (SizeType i = 0; i < patternPropertyCount_; i++)
2015-05-08 13:44:52 +08:00
if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
2015-05-08 13:37:30 +08:00
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
}
2015-05-07 00:59:51 +08:00
SizeType index;
if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
2015-05-08 13:37:30 +08:00
if (context.patternPropertiesSchemaCount > 0) {
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
2015-05-08 13:37:30 +08:00
context.valueSchema = GetTypeless();
2015-05-09 14:22:04 +08:00
context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
2015-05-08 13:37:30 +08:00
}
else
context.valueSchema = properties_[index].schema;
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
if (properties_[index].required)
context.objectRequiredCount++;
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
if (hasDependencies_)
context.objectDependencies[index] = true;
return true;
2015-05-01 17:59:31 +08:00
}
if (additionalPropertiesSchema_) {
2015-05-08 13:37:30 +08:00
if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
context.valueSchema = GetTypeless();
2015-05-09 14:22:04 +08:00
context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
2015-05-08 13:37:30 +08:00
}
else
context.valueSchema = additionalPropertiesSchema_;
2015-05-07 00:59:51 +08:00
return true;
}
else if (additionalProperties_) {
2015-05-07 00:59:51 +08:00
context.valueSchema = GetTypeless();
return true;
}
2015-05-08 13:37:30 +08:00
2015-05-11 23:53:03 +08:00
if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
2015-05-11 23:53:03 +08:00
return true;
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool EndObject(Context& context, SizeType memberCount) const {
2015-05-11 23:53:03 +08:00
if (context.objectRequiredCount != requiredCount_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
2015-05-11 23:53:03 +08:00
if (memberCount < minProperties_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
2015-05-11 23:53:03 +08:00
if (memberCount > maxProperties_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
2015-05-01 17:59:31 +08:00
if (hasDependencies_) {
2015-05-07 00:59:51 +08:00
for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
if (context.objectDependencies[sourceIndex]) {
if (properties_[sourceIndex].dependencies) {
for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex])
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
}
else if (properties_[sourceIndex].dependenciesSchema)
if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
}
}
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
return true;
}
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
bool StartArray(Context& context) const {
2015-05-11 23:53:03 +08:00
if (!(type_ & (1 << kArraySchemaType)))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-10 17:44:43 +08:00
2015-05-07 00:59:51 +08:00
context.arrayElementIndex = 0;
context.inArray = true;
2015-05-11 23:53:03 +08:00
return CreateParallelValidator(context);
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
bool EndArray(Context& context, SizeType elementCount) const {
context.inArray = false;
2015-05-11 23:53:03 +08:00
if (elementCount < minItems_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
2015-05-11 23:53:03 +08:00
if (elementCount > maxItems_)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
2015-05-11 23:53:03 +08:00
return true;
2015-05-07 00:59:51 +08:00
}
2015-05-01 17:59:31 +08:00
// Generate functions for string literal according to Ch
#define RAPIDJSON_STRING_(name, ...) \
static const ValueType& Get##name##String() {\
static const Ch s[] = { __VA_ARGS__, '\0' };\
static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
return v;\
}
RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
RAPIDJSON_STRING_(Not, 'n', 'o', 't')
RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
#undef RAPIDJSON_STRING_
2015-05-07 15:05:55 +08:00
private:
2015-05-09 14:22:04 +08:00
enum SchemaValueType {
kNullSchemaType,
kBooleanSchemaType,
kObjectSchemaType,
kArraySchemaType,
kStringSchemaType,
kNumberSchemaType,
kIntegerSchemaType,
kTotalSchemaType
};
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
typedef internal::GenericRegex<EncodingType> RegexType;
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
2015-05-08 14:09:46 +08:00
typedef std::basic_regex<Ch> RegexType;
2015-05-08 14:03:05 +08:00
#else
typedef char RegexType;
#endif
struct SchemaArray {
SchemaArray() : schemas(), count() {}
2015-05-11 00:37:18 +08:00
~SchemaArray() { AllocatorType::Free(schemas); }
2015-05-09 15:58:01 +08:00
const SchemaType** schemas;
SizeType begin; // begin index of context.validators
SizeType count;
};
static const SchemaType* GetTypeless() {
static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), 0);
2015-05-07 00:59:51 +08:00
return &typeless;
}
2015-05-07 23:05:05 +08:00
template <typename V1, typename V2>
void AddUniqueElement(V1& a, const V2& v) {
for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
if (*itr == v)
return;
2015-05-11 00:37:18 +08:00
V1 c(v, *allocator_);
a.PushBack(c, *allocator_);
2015-05-07 23:05:05 +08:00
}
2015-05-07 15:05:55 +08:00
template <typename ValueType>
static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
2015-05-07 15:05:55 +08:00
typename ValueType::ConstMemberIterator itr = value.FindMember(name);
return itr != value.MemberEnd() ? &(itr->value) : 0;
2015-05-07 00:59:51 +08:00
}
2015-05-01 17:59:31 +08:00
2015-05-07 15:05:55 +08:00
template <typename ValueType>
static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
2015-05-07 15:05:55 +08:00
if (const ValueType* v = GetMember(value, name))
if (v->IsBool())
out = v->GetBool();
}
template <typename ValueType>
static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
2015-05-07 15:05:55 +08:00
if (const ValueType* v = GetMember(value, name))
if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
out = static_cast<SizeType>(v->GetUint64());
}
2015-05-09 14:22:04 +08:00
template <typename DocumentType, typename ValueType, typename PointerType>
void AssignIfExist(SchemaArray& out, const DocumentType& document, const PointerType& p, const ValueType& value, const ValueType& name) {
2015-05-09 08:37:49 +08:00
if (const ValueType* v = GetMember(value, name)) {
2015-05-07 15:05:55 +08:00
if (v->IsArray() && v->Size() > 0) {
PointerType q = p.Append(name, allocator_);
2015-05-07 15:05:55 +08:00
out.count = v->Size();
2015-05-11 00:37:18 +08:00
out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
2015-05-08 16:26:36 +08:00
memset(out.schemas, 0, sizeof(Schema*)* out.count);
2015-05-07 15:05:55 +08:00
for (SizeType i = 0; i < out.count; i++)
document->CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i]);
out.begin = validatorCount_;
validatorCount_ += out.count;
2015-05-07 15:05:55 +08:00
}
2015-05-09 08:37:49 +08:00
}
2015-05-07 15:05:55 +08:00
}
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
template <typename ValueType>
RegexType* CreatePattern(const ValueType& value) {
if (value.IsString()) {
RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
if (!r->IsValid()) {
r->~RegexType();
r = 0;
}
return r;
}
return 0;
}
static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
return pattern->Search(str);
}
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
2015-05-07 15:05:55 +08:00
template <typename ValueType>
2015-05-11 00:37:18 +08:00
RegexType* CreatePattern(const ValueType& value) {
2015-05-07 15:05:55 +08:00
if (value.IsString())
try {
2015-05-11 00:37:18 +08:00
return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
2015-05-07 15:05:55 +08:00
}
catch (const std::regex_error&) {
}
return 0;
}
2015-05-08 14:03:05 +08:00
static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
2015-05-07 15:05:55 +08:00
std::match_results<const Ch*> r;
return std::regex_search(str, str + length, r, *pattern);
2015-05-07 15:05:55 +08:00
}
2015-05-08 13:44:52 +08:00
#else
template <typename ValueType>
2015-05-08 14:03:05 +08:00
RegexType* CreatePattern(const ValueType&) { return 0; }
2015-05-08 13:44:52 +08:00
2015-05-08 14:03:05 +08:00
static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
2015-05-07 15:05:55 +08:00
#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
void AddType(const ValueType& type) {
if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
2015-05-07 00:59:51 +08:00
}
2015-05-01 17:59:31 +08:00
2015-05-11 23:53:03 +08:00
bool CreateParallelValidator(Context& context) const {
2015-05-10 17:44:43 +08:00
if (enum_ || context.arrayUniqueness)
2015-05-17 20:43:52 +08:00
context.hasher = context.factory.CreateHasher();
2015-05-11 23:53:03 +08:00
if (validatorCount_) {
RAPIDJSON_ASSERT(context.validators == 0);
context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
context.validatorCount = validatorCount_;
if (allOf_.schemas)
CreateSchemaValidators(context, allOf_);
2015-05-11 23:53:03 +08:00
if (anyOf_.schemas)
CreateSchemaValidators(context, anyOf_);
if (oneOf_.schemas)
CreateSchemaValidators(context, oneOf_);
if (not_)
context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
if (hasSchemaDependencies_) {
for (SizeType i = 0; i < propertyCount_; i++)
if (properties_[i].dependenciesSchema)
context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
}
}
2015-05-11 23:53:03 +08:00
return true;
2015-05-07 00:59:51 +08:00
}
void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
for (SizeType i = 0; i < schemas.count; i++)
context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
2015-05-07 00:59:51 +08:00
}
2015-05-01 17:59:31 +08:00
2015-05-07 00:59:51 +08:00
// O(n)
template <typename ValueType>
bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
2016-01-30 22:41:09 +08:00
SizeType len = name.GetStringLength();
const Ch* str = name.GetString();
2015-05-07 15:05:55 +08:00
for (SizeType index = 0; index < propertyCount_; index++)
2016-01-30 22:41:09 +08:00
if (properties_[index].name.GetStringLength() == len &&
(std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
{
2015-05-07 00:59:51 +08:00
*outIndex = index;
return true;
2015-05-01 17:59:31 +08:00
}
2015-05-07 00:59:51 +08:00
return false;
}
2015-05-01 17:59:31 +08:00
2015-05-13 09:44:25 +08:00
bool CheckInt(Context& context, int64_t i) const {
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-13 09:44:25 +08:00
if (!minimum_.IsNull()) {
if (minimum_.IsInt64()) {
if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
2015-05-13 09:44:25 +08:00
}
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
return false;
}
if (!maximum_.IsNull()) {
if (maximum_.IsInt64()) {
if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
2015-05-13 09:44:25 +08:00
}
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
return false;
}
if (!multipleOf_.IsNull()) {
if (multipleOf_.IsUint64()) {
if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
2015-05-13 09:44:25 +08:00
}
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
return false;
}
return true;
}
bool CheckUint(Context& context, uint64_t i) const {
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
2015-05-13 09:44:25 +08:00
if (!minimum_.IsNull()) {
if (minimum_.IsUint64()) {
if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
2015-05-13 09:44:25 +08:00
}
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
return false;
}
if (!maximum_.IsNull()) {
if (maximum_.IsUint64()) {
if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
2015-05-13 09:44:25 +08:00
}
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
return false;
}
if (!multipleOf_.IsNull()) {
if (multipleOf_.IsUint64()) {
if (i % multipleOf_.GetUint64() != 0)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
2015-05-13 09:44:25 +08:00
}
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
return false;
}
return true;
}
bool CheckDoubleMinimum(Context& context, double d) const {
if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
2015-05-13 09:44:25 +08:00
return true;
}
bool CheckDoubleMaximum(Context& context, double d) const {
if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
2015-05-13 09:44:25 +08:00
return true;
}
2015-05-11 23:53:03 +08:00
2015-05-13 09:44:25 +08:00
bool CheckDoubleMultipleOf(Context& context, double d) const {
double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
double q = std::floor(a / b);
double r = a - q * b;
if (r > 0.0)
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
2015-05-01 17:59:31 +08:00
return true;
}
2015-05-07 00:59:51 +08:00
struct Property {
Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
2015-05-11 00:37:18 +08:00
~Property() { AllocatorType::Free(dependencies); }
SValue name;
const SchemaType* schema;
const SchemaType* dependenciesSchema;
SizeType dependenciesValidatorIndex;
2015-05-07 00:59:51 +08:00
bool* dependencies;
bool required;
};
struct PatternProperty {
PatternProperty() : schema(), pattern() {}
2015-05-11 00:37:18 +08:00
~PatternProperty() {
if (pattern) {
pattern->~RegexType();
AllocatorType::Free(pattern);
}
}
2015-05-09 14:22:04 +08:00
const SchemaType* schema;
2015-05-11 00:37:18 +08:00
RegexType* pattern;
2015-05-07 00:59:51 +08:00
};
2015-05-11 00:37:18 +08:00
AllocatorType* allocator_;
2015-05-10 12:17:23 +08:00
uint64_t* enum_;
SizeType enumCount_;
SchemaArray allOf_;
SchemaArray anyOf_;
SchemaArray oneOf_;
2015-05-09 14:22:04 +08:00
const SchemaType* not_;
2015-05-07 00:59:51 +08:00
unsigned type_; // bitmask of kSchemaType
SizeType validatorCount_;
SizeType notValidatorIndex_;
2015-05-07 00:59:51 +08:00
Property* properties_;
2015-05-09 14:22:04 +08:00
const SchemaType* additionalPropertiesSchema_;
2015-05-07 00:59:51 +08:00
PatternProperty* patternProperties_;
SizeType patternPropertyCount_;
SizeType propertyCount_;
SizeType requiredCount_;
SizeType minProperties_;
SizeType maxProperties_;
bool additionalProperties_;
2015-05-07 00:59:51 +08:00
bool hasDependencies_;
bool hasSchemaDependencies_;
2015-05-07 00:59:51 +08:00
2015-05-09 14:22:04 +08:00
const SchemaType* additionalItemsSchema_;
const SchemaType* itemsList_;
const SchemaType** itemsTuple_;
2015-05-07 00:59:51 +08:00
SizeType itemsTupleCount_;
SizeType minItems_;
SizeType maxItems_;
bool additionalItems_;
2015-05-10 17:44:43 +08:00
bool uniqueItems_;
2015-05-07 00:59:51 +08:00
2015-05-11 00:37:18 +08:00
RegexType* pattern_;
2015-05-07 00:59:51 +08:00
SizeType minLength_;
SizeType maxLength_;
2015-05-13 09:44:25 +08:00
SValue minimum_;
SValue maximum_;
SValue multipleOf_;
2015-05-01 17:59:31 +08:00
bool exclusiveMinimum_;
bool exclusiveMaximum_;
};
} // namespace internal
2015-05-09 15:58:01 +08:00
///////////////////////////////////////////////////////////////////////////////
// IGenericRemoteSchemaDocumentProvider
2015-05-11 00:37:18 +08:00
template <typename SchemaDocumentType>
2015-05-09 15:58:01 +08:00
class IGenericRemoteSchemaDocumentProvider {
public:
2015-05-11 00:37:18 +08:00
typedef typename SchemaDocumentType::Ch Ch;
2015-05-09 15:58:01 +08:00
virtual ~IGenericRemoteSchemaDocumentProvider() {}
2015-05-09 21:36:30 +08:00
virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
2015-05-09 15:58:01 +08:00
};
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// GenericSchemaDocument
2015-05-17 20:43:52 +08:00
template <typename ValueT, typename Allocator = CrtAllocator>
2015-05-08 16:26:36 +08:00
class GenericSchemaDocument {
2015-05-01 17:59:31 +08:00
public:
2015-05-09 15:58:01 +08:00
typedef ValueT ValueType;
2015-05-11 00:37:18 +08:00
typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
2015-05-09 15:58:01 +08:00
typedef Allocator AllocatorType;
typedef typename ValueType::EncodingType EncodingType;
typedef typename EncodingType::Ch Ch;
typedef internal::Schema<GenericSchemaDocument> SchemaType;
typedef GenericPointer<ValueType, Allocator> PointerType;
2015-05-09 15:58:01 +08:00
friend class internal::Schema<GenericSchemaDocument>;
2015-05-11 13:55:34 +08:00
template <typename, typename, typename>
friend class GenericSchemaValidator;
2015-05-09 15:58:01 +08:00
GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
2015-05-15 19:53:22 +08:00
document_(&document),
2015-05-09 15:58:01 +08:00
remoteProvider_(remoteProvider),
2015-05-11 00:37:18 +08:00
allocator_(allocator),
ownAllocator_(),
2015-05-09 15:58:01 +08:00
root_(),
schemaMap_(allocator, kInitialSchemaMapSize),
schemaRef_(allocator, kInitialSchemaRefSize)
{
2015-05-11 00:37:18 +08:00
if (!allocator_)
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
2015-05-09 11:46:45 +08:00
// Generate root schema, it will call CreateSchema() to create sub-schemas,
// And call AddRefSchema() if there are $ref.
2015-05-15 19:53:22 +08:00
CreateSchemaRecursive(&root_, PointerType(), static_cast<const ValueType&>(document));
2015-05-09 11:46:45 +08:00
// Resolve $ref
while (!schemaRef_.Empty()) {
2015-05-15 19:53:22 +08:00
SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
if (const SchemaType* s = GetSchema(refEntry->target)) {
2015-05-16 16:15:00 +08:00
if (refEntry->schema)
*refEntry->schema = s;
2015-05-15 19:53:22 +08:00
// Create entry in map if not exist
if (!GetSchema(refEntry->source)) {
new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
2015-05-15 19:53:22 +08:00
}
}
refEntry->~SchemaRefEntry();
}
2015-05-15 19:53:22 +08:00
RAPIDJSON_ASSERT(root_ != 0);
2015-05-11 00:37:18 +08:00
schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
2015-05-01 17:59:31 +08:00
}
2015-05-08 16:26:36 +08:00
~GenericSchemaDocument() {
2015-05-15 21:18:52 +08:00
while (!schemaMap_.Empty())
schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
2015-05-11 00:37:18 +08:00
RAPIDJSON_DELETE(ownAllocator_);
2015-05-01 17:59:31 +08:00
}
2015-05-09 08:37:49 +08:00
const SchemaType& GetRoot() const { return *root_; }
private:
2015-05-15 19:53:22 +08:00
struct SchemaRefEntry {
SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
2015-05-15 19:53:22 +08:00
PointerType source;
PointerType target;
const SchemaType** schema;
};
2015-05-09 08:37:49 +08:00
struct SchemaEntry {
SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
2015-05-15 21:18:52 +08:00
~SchemaEntry() {
if (owned) {
2015-05-15 21:18:52 +08:00
schema->~SchemaType();
Allocator::Free(schema);
}
2015-05-15 21:18:52 +08:00
}
2015-05-16 16:15:00 +08:00
PointerType pointer;
2015-05-09 08:37:49 +08:00
SchemaType* schema;
2015-05-15 19:53:22 +08:00
bool owned;
2015-05-09 08:37:49 +08:00
};
2015-05-15 19:53:22 +08:00
void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v) {
2015-05-15 21:18:52 +08:00
if (schema)
*schema = SchemaType::GetTypeless();
2015-05-15 19:53:22 +08:00
2015-05-09 21:36:30 +08:00
if (v.GetType() == kObjectType) {
const SchemaType* s = GetSchema(pointer);
if (!s)
2015-05-15 19:53:22 +08:00
CreateSchema(schema, pointer, v);
2015-05-15 21:18:52 +08:00
else if (schema)
2015-05-15 19:53:22 +08:00
*schema = s;
2015-05-09 21:36:30 +08:00
for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value);
2015-05-09 21:36:30 +08:00
}
2015-05-15 21:18:52 +08:00
else if (v.GetType() == kArrayType)
2015-05-09 21:36:30 +08:00
for (SizeType i = 0; i < v.Size(); i++)
CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i]);
2015-05-09 21:36:30 +08:00
}
2015-05-15 19:53:22 +08:00
void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v) {
2015-05-09 11:46:45 +08:00
RAPIDJSON_ASSERT(pointer.IsValid());
2015-05-15 19:53:22 +08:00
if (v.IsObject()) {
2015-05-16 16:15:00 +08:00
if (!HandleRefSchema(pointer, schema, v)) {
2015-05-16 10:25:10 +08:00
SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, allocator_);
new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
2015-05-15 21:18:52 +08:00
if (schema)
*schema = s;
2015-05-15 19:53:22 +08:00
}
}
}
bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v) {
static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
static const ValueType kRefValue(kRefString, 4);
typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
2015-05-15 19:53:22 +08:00
if (itr == v.MemberEnd())
return false;
if (itr->value.IsString()) {
SizeType len = itr->value.GetStringLength();
2015-05-09 15:58:01 +08:00
if (len > 0) {
2015-05-15 19:53:22 +08:00
const Ch* s = itr->value.GetString();
2015-05-09 15:58:01 +08:00
SizeType i = 0;
while (i < len && s[i] != '#') // Find the first #
i++;
2015-05-09 20:24:47 +08:00
if (i > 0) { // Remote reference, resolve immediately
if (remoteProvider_) {
2015-05-09 21:36:30 +08:00
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
PointerType pointer(&s[i], len - i, allocator_);
2015-05-15 19:53:22 +08:00
if (pointer.IsValid()) {
if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
2015-05-16 16:15:00 +08:00
if (schema)
*schema = sc;
2015-05-15 19:53:22 +08:00
return true;
}
}
2015-05-09 15:58:01 +08:00
}
}
2015-05-09 20:24:47 +08:00
}
else if (s[i] == '#') { // Local reference, defer resolution
PointerType pointer(&s[i], len - i, allocator_);
2015-05-15 19:53:22 +08:00
if (pointer.IsValid()) {
if (const ValueType* nv = pointer.Get(*document_))
if (HandleRefSchema(source, schema, *nv))
return true;
new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
2015-05-15 19:53:22 +08:00
return true;
}
2015-05-09 15:58:01 +08:00
}
}
2015-05-09 11:46:45 +08:00
}
2015-05-15 19:53:22 +08:00
return false;
2015-05-09 11:46:45 +08:00
}
2015-05-09 21:36:30 +08:00
const SchemaType* GetSchema(const PointerType& pointer) const {
for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
2015-05-16 16:15:00 +08:00
if (pointer == target->pointer)
2015-05-09 20:24:47 +08:00
return target->schema;
2015-05-09 21:36:30 +08:00
return 0;
2015-05-09 15:58:01 +08:00
}
2015-05-11 13:55:34 +08:00
PointerType GetPointer(const SchemaType* schema) const {
2015-05-16 16:15:00 +08:00
for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
if (schema == target->schema)
2015-05-16 16:15:00 +08:00
return target->pointer;
return PointerType();
2015-05-11 13:55:34 +08:00
}
2015-05-11 00:37:18 +08:00
static const size_t kInitialSchemaMapSize = 64;
static const size_t kInitialSchemaRefSize = 64;
2015-05-09 08:37:49 +08:00
2015-05-15 19:53:22 +08:00
const ValueType* document_; //!< Only temporarily for constructor
2015-05-09 15:58:01 +08:00
IRemoteSchemaDocumentProviderType* remoteProvider_;
2015-05-11 00:37:18 +08:00
Allocator *allocator_;
Allocator *ownAllocator_;
2015-05-09 14:22:04 +08:00
const SchemaType* root_; //!< Root schema.
2015-05-09 11:46:45 +08:00
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
2015-05-01 17:59:31 +08:00
};
2015-05-09 15:58:01 +08:00
typedef GenericSchemaDocument<Value> SchemaDocument;
2015-05-11 00:37:18 +08:00
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
2015-05-01 17:59:31 +08:00
2015-05-09 14:22:04 +08:00
///////////////////////////////////////////////////////////////////////////////
// GenericSchemaValidator
2015-05-09 15:58:01 +08:00
template <typename SchemaDocumentType, typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>, typename StateAllocator = CrtAllocator >
class GenericSchemaValidator :
public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
2015-05-09 15:58:01 +08:00
public internal::ISchemaValidator
{
2015-05-01 17:59:31 +08:00
public:
2015-05-09 15:58:01 +08:00
typedef typename SchemaDocumentType::SchemaType SchemaType;
2015-05-11 13:55:34 +08:00
typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename SchemaType::EncodingType EncodingType;
typedef typename EncodingType::Ch Ch;
2015-05-01 17:59:31 +08:00
GenericSchemaValidator(
const SchemaDocumentType& schemaDocument,
StateAllocator* allocator = 0,
2015-05-11 13:55:34 +08:00
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2015-05-01 17:59:31 +08:00
:
2015-05-11 13:55:34 +08:00
schemaDocument_(&schemaDocument),
2015-05-09 08:37:49 +08:00
root_(schemaDocument.GetRoot()),
2015-05-01 17:59:31 +08:00
outputHandler_(nullOutputHandler_),
stateAllocator_(allocator),
ownStateAllocator_(0),
schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity),
valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0)
#endif
{
CreateOwnAllocator();
}
// Constructor with outputHandler
GenericSchemaValidator(
const SchemaDocumentType& schemaDocument,
OutputHandler& outputHandler,
StateAllocator* allocator = 0,
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
:
schemaDocument_(&schemaDocument),
root_(schemaDocument.GetRoot()),
outputHandler_(outputHandler),
stateAllocator_(allocator),
ownStateAllocator_(0),
schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity),
valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0)
#endif
2015-05-01 17:59:31 +08:00
{
CreateOwnAllocator();
}
~GenericSchemaValidator() {
2015-05-01 17:59:31 +08:00
Reset();
2016-01-27 14:17:24 +08:00
RAPIDJSON_DELETE(ownStateAllocator_);
2015-05-01 17:59:31 +08:00
}
void Reset() {
while (!schemaStack_.Empty())
PopSchema();
2015-05-05 16:44:05 +08:00
//documentStack_.Clear();
valid_ = true;
}
2015-05-01 17:59:31 +08:00
// Implementation of ISchemaValidator
virtual bool IsValid() const { return valid_; }
2015-05-11 13:55:34 +08:00
PointerType GetInvalidSchemaPointer() const {
2015-05-16 16:15:00 +08:00
return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
2015-05-11 13:55:34 +08:00
}
const Ch* GetInvalidSchemaKeyword() const {
2015-05-11 23:53:03 +08:00
return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
}
2015-05-11 13:55:34 +08:00
PointerType GetInvalidDocumentPointer() const {
return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
}
StateAllocator& GetStateAllocator() {
return *stateAllocator_;
}
2015-05-16 10:52:16 +08:00
#if RAPIDJSON_SCHEMA_VERBOSE
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
RAPIDJSON_MULTILINEMACRO_BEGIN\
*documentStack_.template Push<Ch>() = '\0';\
documentStack_.template Pop<Ch>(1);\
internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
2015-05-16 10:52:16 +08:00
RAPIDJSON_MULTILINEMACRO_END
#else
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
#endif
2015-05-07 00:59:51 +08:00
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
if (!valid_) return false; \
2015-05-16 10:52:16 +08:00
if (!BeginValue() || !CurrentSchema().method arg1) {\
RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
return valid_ = false;\
}
2015-05-08 13:37:30 +08:00
#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
2015-05-09 21:36:30 +08:00
for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
2015-05-10 12:17:23 +08:00
if (context->hasher)\
static_cast<HasherType*>(context->hasher)->method arg2;\
if (context->validators)\
for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
if (context->patternPropertiesValidators)\
for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
}
#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
return valid_ = EndValue() && outputHandler_.method arg2
2015-05-07 00:59:51 +08:00
#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
2015-05-07 10:26:13 +08:00
bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
bool String(const Ch* str, SizeType length, bool copy)
2015-05-07 00:59:51 +08:00
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2015-05-07 10:26:13 +08:00
bool StartObject() {
2015-05-07 00:59:51 +08:00
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
return valid_ = outputHandler_.StartObject();
}
2015-05-07 10:26:13 +08:00
bool Key(const Ch* str, SizeType len, bool copy) {
if (!valid_) return false;
2015-05-11 13:55:34 +08:00
AppendToken(str, len);
if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
return valid_ = outputHandler_.Key(str, len, copy);
}
2015-05-07 10:26:13 +08:00
bool EndObject(SizeType memberCount) {
if (!valid_) return false;
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
}
2015-05-07 10:26:13 +08:00
bool StartArray() {
2015-05-07 00:59:51 +08:00
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
return valid_ = outputHandler_.StartArray();
}
2015-05-07 10:26:13 +08:00
bool EndArray(SizeType elementCount) {
if (!valid_) return false;
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
2015-05-08 13:37:30 +08:00
RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
}
2015-05-16 16:15:00 +08:00
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
2015-05-08 13:37:30 +08:00
#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
2015-05-05 16:44:05 +08:00
// Implementation of ISchemaStateFactory<SchemaType>
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
#if RAPIDJSON_SCHEMA_VERBOSE
depth_ + 1,
#endif
&GetStateAllocator());
}
virtual void DestroySchemaValidator(ISchemaValidator* validator) {
GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
v->~GenericSchemaValidator();
StateAllocator::Free(v);
2015-05-05 16:44:05 +08:00
}
2015-05-01 17:59:31 +08:00
2015-05-17 20:43:52 +08:00
virtual void* CreateHasher() {
return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
}
virtual uint64_t GetHashCode(void* hasher) {
return static_cast<HasherType*>(hasher)->GetHashCode();
}
2015-05-17 20:43:52 +08:00
virtual void DestroryHasher(void* hasher) {
HasherType* h = static_cast<HasherType*>(hasher);
h->~HasherType();
StateAllocator::Free(h);
}
virtual void* MallocState(size_t size) {
return GetStateAllocator().Malloc(size);
}
virtual void* ReallocState(void* originalPtr, size_t originalSize, size_t newSize) {
return GetStateAllocator().Realloc(originalPtr, originalSize, newSize);
}
virtual void FreeState(void* p) {
return StateAllocator::Free(p);
}
2015-05-01 17:59:31 +08:00
private:
typedef typename SchemaType::Context Context;
typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
2015-05-01 17:59:31 +08:00
2015-05-05 16:44:05 +08:00
GenericSchemaValidator(
const SchemaDocumentType& schemaDocument,
const SchemaType& root,
#if RAPIDJSON_SCHEMA_VERBOSE
unsigned depth,
#endif
StateAllocator* allocator = 0,
2015-05-11 13:55:34 +08:00
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2015-05-05 16:44:05 +08:00
:
schemaDocument_(&schemaDocument),
2015-05-05 16:44:05 +08:00
root_(root),
outputHandler_(nullOutputHandler_),
stateAllocator_(allocator),
ownStateAllocator_(0),
schemaStack_(allocator, schemaStackCapacity),
2015-05-11 13:55:34 +08:00
documentStack_(allocator, documentStackCapacity),
valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(depth)
#endif
2015-05-05 16:44:05 +08:00
{
CreateOwnAllocator();
}
void CreateOwnAllocator() {
if (!stateAllocator_)
2016-01-27 14:17:24 +08:00
stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
2015-05-05 16:44:05 +08:00
}
2015-05-07 00:59:51 +08:00
bool BeginValue() {
2015-05-02 17:46:55 +08:00
if (schemaStack_.Empty())
2015-05-05 16:44:05 +08:00
PushSchema(root_);
2015-05-01 17:59:31 +08:00
else {
2015-05-11 13:55:34 +08:00
if (CurrentContext().inArray)
2016-01-30 22:41:09 +08:00
AppendToken<Ch>(CurrentContext().arrayElementIndex);
2015-05-11 13:55:34 +08:00
2015-05-02 10:06:48 +08:00
if (!CurrentSchema().BeginValue(CurrentContext()))
return false;
2015-05-08 13:37:30 +08:00
SizeType count = CurrentContext().patternPropertiesSchemaCount;
const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
2015-05-09 14:22:04 +08:00
typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
2015-05-10 17:44:43 +08:00
bool valueUniqueness = CurrentContext().valueUniqueness;
2015-05-01 17:59:31 +08:00
if (CurrentContext().valueSchema)
PushSchema(*CurrentContext().valueSchema);
2015-05-08 13:37:30 +08:00
if (count > 0) {
CurrentContext().objectPatternValidatorType = patternValidatorType;
ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
2015-05-08 13:37:30 +08:00
for (SizeType i = 0; i < count; i++)
va[validatorCount++] = CreateSchemaValidator(*sa[i]);
2015-05-08 13:37:30 +08:00
}
2015-05-10 17:44:43 +08:00
CurrentContext().arrayUniqueness = valueUniqueness;
2015-05-01 17:59:31 +08:00
}
2015-05-02 17:46:55 +08:00
return true;
2015-05-01 17:59:31 +08:00
}
2015-05-02 12:24:23 +08:00
bool EndValue() {
if (!CurrentSchema().EndValue(CurrentContext()))
return false;
#if RAPIDJSON_SCHEMA_VERBOSE
GenericStringBuffer<EncodingType> sb;
2015-05-16 16:15:00 +08:00
schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
*documentStack_.template Push<Ch>() = '\0';
documentStack_.template Pop<Ch>(1);
internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
#endif
uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
2015-05-10 17:44:43 +08:00
2015-05-02 12:24:23 +08:00
PopSchema();
2015-05-10 17:44:43 +08:00
if (!schemaStack_.Empty()) {
Context& context = CurrentContext();
if (context.valueUniqueness) {
HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
if (!a)
CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
2015-05-10 17:44:43 +08:00
if (itr->GetUint64() == h)
RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
a->PushBack(h, GetStateAllocator());
2015-05-10 17:44:43 +08:00
}
}
// Remove the last token of document pointer
2015-05-11 23:53:03 +08:00
while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
;
2015-05-02 12:24:23 +08:00
return true;
}
2015-05-11 13:55:34 +08:00
void AppendToken(const Ch* str, SizeType len) {
2016-01-30 22:41:09 +08:00
documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
*documentStack_.template PushUnsafe<Ch>() = '/';
2015-05-11 13:55:34 +08:00
for (SizeType i = 0; i < len; i++) {
if (str[i] == '~') {
2016-01-30 22:41:09 +08:00
*documentStack_.template PushUnsafe<Ch>() = '~';
*documentStack_.template PushUnsafe<Ch>() = '0';
2015-05-11 13:55:34 +08:00
}
else if (str[i] == '/') {
2016-01-30 22:41:09 +08:00
*documentStack_.template PushUnsafe<Ch>() = '~';
*documentStack_.template PushUnsafe<Ch>() = '1';
2015-05-11 13:55:34 +08:00
}
else
2016-01-30 22:41:09 +08:00
*documentStack_.template PushUnsafe<Ch>() = str[i];
2015-05-11 13:55:34 +08:00
}
}
2016-01-30 22:41:09 +08:00
template<typename Ch>
2015-05-11 13:55:34 +08:00
void AppendToken(SizeType index) {
*documentStack_.template Push<Ch>() = '/';
char buffer[21];
size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer)) - buffer);
for (size_t i = 0; i < length; i++)
2015-05-11 13:55:34 +08:00
*documentStack_.template Push<Ch>() = buffer[i];
}
2016-01-30 22:41:09 +08:00
// Specialized version for char to prevent buffer copying.
template <>
void AppendToken<char>(SizeType index) {
if (sizeof(SizeType) == 4) {
char *buffer = documentStack_.template Push<Ch>(1 + 10); // '/' + uint
*buffer++ = '/';
const char* end = internal::u32toa(index, buffer);
documentStack_.template Pop<Ch>(static_cast<size_t>(10 - (end - buffer)));
}
else {
char *buffer = documentStack_.template Push<Ch>(1 + 20); // '/' + uint64
*buffer++ = '/';
const char* end = internal::u64toa(index, buffer);
documentStack_.template Pop<Ch>(static_cast<size_t>(20 - (end - buffer)));
}
}
RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
2016-01-30 22:41:09 +08:00
RAPIDJSON_FORCEINLINE void PopSchema() {
Context* c = schemaStack_.template Pop<Context>(1);
if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
a->~HashCodeArray();
StateAllocator::Free(a);
}
c->~Context();
}
2015-05-11 13:55:34 +08:00
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
2015-05-01 20:16:41 +08:00
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
2015-05-11 23:53:03 +08:00
const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
2015-05-01 17:59:31 +08:00
2015-05-08 13:37:30 +08:00
static const size_t kDefaultSchemaStackCapacity = 1024;
2015-05-11 13:55:34 +08:00
static const size_t kDefaultDocumentStackCapacity = 256;
const SchemaDocumentType* schemaDocument_;
const SchemaType& root_;
OutputHandler nullOutputHandler_;
2015-05-01 17:59:31 +08:00
OutputHandler& outputHandler_;
StateAllocator* stateAllocator_;
StateAllocator* ownStateAllocator_;
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
2015-05-11 13:55:34 +08:00
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
bool valid_;
#if RAPIDJSON_SCHEMA_VERBOSE
unsigned depth_;
#endif
2015-05-01 17:59:31 +08:00
};
2015-05-09 15:58:01 +08:00
typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
2015-05-01 17:59:31 +08:00
template <
unsigned parseFlags,
typename InputStream,
typename SourceEncoding,
typename SchemaDocumentType = SchemaDocument,
typename StackAllocator = CrtAllocator>
class SchemaValidatingReader {
public:
typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename InputStream::Ch Ch;
SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_() {}
template <typename Handler>
bool operator()(Handler& handler) {
GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
parseResult_ = reader.template Parse<parseFlags>(is_, validator);
if (validator.IsValid()) {
invalidSchemaPointer_ = PointerType();
invalidSchemaKeyword_ = 0;
invalidDocumentPointer_ = PointerType();
}
else {
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
}
return parseResult_;
}
const ParseResult& GetParseResult() const { return parseResult_; }
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
private:
InputStream& is_;
const SchemaDocumentType& sd_;
ParseResult parseResult_;
PointerType invalidSchemaPointer_;
const Ch* invalidSchemaKeyword_;
PointerType invalidDocumentPointer_;
};
2015-05-01 17:59:31 +08:00
RAPIDJSON_NAMESPACE_END
2015-05-01 22:38:00 +08:00
#if defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
2015-05-01 17:59:31 +08:00
#endif // RAPIDJSON_SCHEMA_H_