1 // Tencent is pleased to support the open source community by making RapidJSON available->
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
5 // Licensed under the MIT License (the "License"); you may not use this file except
6 // in compliance with the License-> You may obtain a copy of the License at
8 // http://opensource->org/licenses/MIT
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied-> See the License for the
13 // specific language governing permissions and limitations under the License->
15 #ifndef RAPIDJSON_SCHEMA_H_
16 #define RAPIDJSON_SCHEMA_H_
20 #include <cmath> // abs, floor
22 #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
23 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
25 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
28 #if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
29 #define RAPIDJSON_SCHEMA_USE_STDREGEX 1
31 #define RAPIDJSON_SCHEMA_USE_STDREGEX 0
34 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
35 #include "internal/regex.h"
36 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
40 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
41 #define RAPIDJSON_SCHEMA_HAS_REGEX 1
43 #define RAPIDJSON_SCHEMA_HAS_REGEX 0
46 #ifndef RAPIDJSON_SCHEMA_VERBOSE
47 #define RAPIDJSON_SCHEMA_VERBOSE 0
50 #if RAPIDJSON_SCHEMA_VERBOSE
51 #include "stringbuffer.h"
57 RAPIDJSON_DIAG_OFF(effc++)
61 RAPIDJSON_DIAG_OFF(weak-vtables)
62 RAPIDJSON_DIAG_OFF(exit-time-destructors)
63 RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
64 RAPIDJSON_DIAG_OFF(variadic-macros)
68 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
71 RAPIDJSON_NAMESPACE_BEGIN
73 ///////////////////////////////////////////////////////////////////////////////
76 #if RAPIDJSON_SCHEMA_VERBOSE
80 inline void PrintInvalidKeyword(const char* keyword) {
81 printf("Fail keyword: %s\n", keyword);
84 inline void PrintInvalidKeyword(const wchar_t* keyword) {
85 wprintf(L"Fail keyword: %ls\n", keyword);
88 inline void PrintInvalidDocument(const char* document) {
89 printf("Fail document: %s\n\n", document);
92 inline void PrintInvalidDocument(const wchar_t* document) {
93 wprintf(L"Fail document: %ls\n\n", document);
96 inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
97 printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
100 inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
101 wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
104 } // namespace internal
106 #endif // RAPIDJSON_SCHEMA_VERBOSE
108 ///////////////////////////////////////////////////////////////////////////////
109 // RAPIDJSON_INVALID_KEYWORD_RETURN
111 #if RAPIDJSON_SCHEMA_VERBOSE
112 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
114 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
117 #define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
118 RAPIDJSON_MULTILINEMACRO_BEGIN\
119 context.invalidKeyword = keyword.GetString();\
120 RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
122 RAPIDJSON_MULTILINEMACRO_END
124 ///////////////////////////////////////////////////////////////////////////////
125 // Forward declarations
127 template <typename ValueType, typename Allocator>
128 class GenericSchemaDocument;
132 template <typename SchemaDocumentType>
135 ///////////////////////////////////////////////////////////////////////////////
138 class ISchemaValidator {
140 virtual ~ISchemaValidator() {}
141 virtual bool IsValid() const = 0;
144 ///////////////////////////////////////////////////////////////////////////////
145 // ISchemaStateFactory
147 template <typename SchemaType>
148 class ISchemaStateFactory {
150 virtual ~ISchemaStateFactory() {}
151 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
152 virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
153 virtual void* CreateHasher() = 0;
154 virtual uint64_t GetHashCode(void* hasher) = 0;
155 virtual void DestroryHasher(void* hasher) = 0;
156 virtual void* MallocState(size_t size) = 0;
157 virtual void FreeState(void* p) = 0;
160 ///////////////////////////////////////////////////////////////////////////////
163 // For comparison of compound value
164 template<typename Encoding, typename Allocator>
167 typedef typename Encoding::Ch Ch;
169 Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
171 bool Null() { return WriteType(kNullType); }
172 bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
173 bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
174 bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
175 bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
176 bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
177 bool Double(double d) {
179 if (d < 0) n.u.i = static_cast<int64_t>(d);
180 else n.u.u = static_cast<uint64_t>(d);
182 return WriteNumber(n);
185 bool RawNumber(const Ch* str, SizeType len, bool) {
186 WriteBuffer(kNumberType, str, len * sizeof(Ch));
190 bool String(const Ch* str, SizeType len, bool) {
191 WriteBuffer(kStringType, str, len * sizeof(Ch));
195 bool StartObject() { return true; }
196 bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
197 bool EndObject(SizeType memberCount) {
198 uint64_t h = Hash(0, kObjectType);
199 uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
200 for (SizeType i = 0; i < memberCount; i++)
201 h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
202 *stack_.template Push<uint64_t>() = h;
206 bool StartArray() { return true; }
207 bool EndArray(SizeType elementCount) {
208 uint64_t h = Hash(0, kArrayType);
209 uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
210 for (SizeType i = 0; i < elementCount; i++)
211 h = Hash(h, e[i]); // Use hash to achieve element order sensitive
212 *stack_.template Push<uint64_t>() = h;
216 bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
218 uint64_t GetHashCode() const {
219 RAPIDJSON_ASSERT(IsValid());
220 return *stack_.template Top<uint64_t>();
224 static const size_t kDefaultSize = 256;
233 bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
235 bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
237 bool WriteBuffer(Type type, const void* data, size_t len) {
238 // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
239 uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
240 const unsigned char* d = static_cast<const unsigned char*>(data);
241 for (size_t i = 0; i < len; i++)
243 *stack_.template Push<uint64_t>() = h;
247 static uint64_t Hash(uint64_t h, uint64_t d) {
248 static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
254 Stack<Allocator> stack_;
257 ///////////////////////////////////////////////////////////////////////////////
258 // SchemaValidationContext
260 template <typename SchemaDocumentType>
261 struct SchemaValidationContext {
262 typedef Schema<SchemaDocumentType> SchemaType;
263 typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
264 typedef typename SchemaType::ValueType ValueType;
265 typedef typename ValueType::Ch Ch;
267 enum PatternValidatorType {
268 kPatternValidatorOnly,
269 kPatternValidatorWithProperty,
270 kPatternValidatorWithAdditionalProperty
273 SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
279 arrayElementHashCodes(),
282 patternPropertiesValidators(),
283 patternPropertiesValidatorCount(),
284 patternPropertiesSchemas(),
285 patternPropertiesSchemaCount(),
286 valuePatternValidatorType(kPatternValidatorOnly),
289 valueUniqueness(false),
290 arrayUniqueness(false)
294 ~SchemaValidationContext() {
296 factory.DestroryHasher(hasher);
298 for (SizeType i = 0; i < validatorCount; i++)
299 factory.DestroySchemaValidator(validators[i]);
300 factory.FreeState(validators);
302 if (patternPropertiesValidators) {
303 for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
304 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
305 factory.FreeState(patternPropertiesValidators);
307 if (patternPropertiesSchemas)
308 factory.FreeState(patternPropertiesSchemas);
310 factory.FreeState(propertyExist);
313 SchemaValidatorFactoryType& factory;
314 const SchemaType* schema;
315 const SchemaType* valueSchema;
316 const Ch* invalidKeyword;
317 void* hasher; // Only validator access
318 void* arrayElementHashCodes; // Only validator access this
319 ISchemaValidator** validators;
320 SizeType validatorCount;
321 ISchemaValidator** patternPropertiesValidators;
322 SizeType patternPropertiesValidatorCount;
323 const SchemaType** patternPropertiesSchemas;
324 SizeType patternPropertiesSchemaCount;
325 PatternValidatorType valuePatternValidatorType;
326 PatternValidatorType objectPatternValidatorType;
327 SizeType arrayElementIndex;
330 bool valueUniqueness;
331 bool arrayUniqueness;
334 ///////////////////////////////////////////////////////////////////////////////
337 template <typename SchemaDocumentType>
340 typedef typename SchemaDocumentType::ValueType ValueType;
341 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
342 typedef typename SchemaDocumentType::PointerType PointerType;
343 typedef typename ValueType::EncodingType EncodingType;
344 typedef typename EncodingType::Ch Ch;
345 typedef SchemaValidationContext<SchemaDocumentType> Context;
346 typedef Schema<SchemaDocumentType> SchemaType;
347 typedef GenericValue<EncodingType, AllocatorType> SValue;
348 friend class GenericSchemaDocument<ValueType, AllocatorType>;
350 Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
351 allocator_(allocator),
355 type_((1 << kTotalSchemaType) - 1), // typeless
358 additionalPropertiesSchema_(),
359 patternProperties_(),
360 patternPropertyCount_(),
363 maxProperties_(SizeType(~0)),
364 additionalProperties_(true),
367 hasSchemaDependencies_(),
368 additionalItemsSchema_(),
373 maxItems_(SizeType(~0)),
374 additionalItems_(true),
378 maxLength_(~SizeType(0)),
379 exclusiveMinimum_(false),
380 exclusiveMaximum_(false)
382 typedef typename SchemaDocumentType::ValueType ValueType;
383 typedef typename ValueType::ConstValueIterator ConstValueIterator;
384 typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
386 if (!value.IsObject())
389 if (const ValueType* v = GetMember(value, GetTypeString())) {
393 else if (v->IsArray())
394 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
398 if (const ValueType* v = GetMember(value, GetEnumString()))
399 if (v->IsArray() && v->Size() > 0) {
400 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
401 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
402 typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
403 char buffer[256 + 24];
404 MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
405 EnumHasherType h(&hasherAllocator, 256);
407 enum_[enumCount_++] = h.GetHashCode();
411 if (schemaDocument) {
412 AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
413 AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
414 AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
417 if (const ValueType* v = GetMember(value, GetNotString())) {
418 schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document);
419 notValidatorIndex_ = validatorCount_;
425 const ValueType* properties = GetMember(value, GetPropertiesString());
426 const ValueType* required = GetMember(value, GetRequiredString());
427 const ValueType* dependencies = GetMember(value, GetDependenciesString());
429 // Gather properties from properties/required/dependencies
430 SValue allProperties(kArrayType);
432 if (properties && properties->IsObject())
433 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
434 AddUniqueElement(allProperties, itr->name);
436 if (required && required->IsArray())
437 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
439 AddUniqueElement(allProperties, *itr);
441 if (dependencies && dependencies->IsObject())
442 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
443 AddUniqueElement(allProperties, itr->name);
444 if (itr->value.IsArray())
445 for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
447 AddUniqueElement(allProperties, *i);
450 if (allProperties.Size() > 0) {
451 propertyCount_ = allProperties.Size();
452 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
453 for (SizeType i = 0; i < propertyCount_; i++) {
454 new (&properties_[i]) Property();
455 properties_[i].name = allProperties[i];
456 properties_[i].schema = GetTypeless();
461 if (properties && properties->IsObject()) {
462 PointerType q = p.Append(GetPropertiesString(), allocator_);
463 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
465 if (FindPropertyIndex(itr->name, &index))
466 schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
470 if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
471 PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
472 patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
473 patternPropertyCount_ = 0;
475 for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
476 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
477 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
478 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
479 patternPropertyCount_++;
483 if (required && required->IsArray())
484 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
485 if (itr->IsString()) {
487 if (FindPropertyIndex(*itr, &index)) {
488 properties_[index].required = true;
493 if (dependencies && dependencies->IsObject()) {
494 PointerType q = p.Append(GetDependenciesString(), allocator_);
495 hasDependencies_ = true;
496 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
497 SizeType sourceIndex;
498 if (FindPropertyIndex(itr->name, &sourceIndex)) {
499 if (itr->value.IsArray()) {
500 properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
501 std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
502 for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
503 SizeType targetIndex;
504 if (FindPropertyIndex(*targetItr, &targetIndex))
505 properties_[sourceIndex].dependencies[targetIndex] = true;
508 else if (itr->value.IsObject()) {
509 hasSchemaDependencies_ = true;
510 schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
511 properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
518 if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
520 additionalProperties_ = v->GetBool();
521 else if (v->IsObject())
522 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
525 AssignIfExist(minProperties_, value, GetMinPropertiesString());
526 AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
529 if (const ValueType* v = GetMember(value, GetItemsString())) {
530 PointerType q = p.Append(GetItemsString(), allocator_);
531 if (v->IsObject()) // List validation
532 schemaDocument->CreateSchema(&itemsList_, q, *v, document);
533 else if (v->IsArray()) { // Tuple validation
534 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
536 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
537 schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
541 AssignIfExist(minItems_, value, GetMinItemsString());
542 AssignIfExist(maxItems_, value, GetMaxItemsString());
544 if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
546 additionalItems_ = v->GetBool();
547 else if (v->IsObject())
548 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
551 AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
554 AssignIfExist(minLength_, value, GetMinLengthString());
555 AssignIfExist(maxLength_, value, GetMaxLengthString());
557 if (const ValueType* v = GetMember(value, GetPatternString()))
558 pattern_ = CreatePattern(*v);
561 if (const ValueType* v = GetMember(value, GetMinimumString()))
563 minimum_.CopyFrom(*v, *allocator_);
565 if (const ValueType* v = GetMember(value, GetMaximumString()))
567 maximum_.CopyFrom(*v, *allocator_);
569 AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
570 AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
572 if (const ValueType* v = GetMember(value, GetMultipleOfString()))
573 if (v->IsNumber() && v->GetDouble() > 0.0)
574 multipleOf_.CopyFrom(*v, *allocator_);
579 allocator_->Free(enum_);
582 for (SizeType i = 0; i < propertyCount_; i++)
583 properties_[i].~Property();
584 AllocatorType::Free(properties_);
586 if (patternProperties_) {
587 for (SizeType i = 0; i < patternPropertyCount_; i++)
588 patternProperties_[i].~PatternProperty();
589 AllocatorType::Free(patternProperties_);
591 AllocatorType::Free(itemsTuple_);
592 #if RAPIDJSON_SCHEMA_HAS_REGEX
594 pattern_->~RegexType();
595 allocator_->Free(pattern_);
600 bool BeginValue(Context& context) const {
601 if (context.inArray) {
603 context.valueUniqueness = true;
606 context.valueSchema = itemsList_;
607 else if (itemsTuple_) {
608 if (context.arrayElementIndex < itemsTupleCount_)
609 context.valueSchema = itemsTuple_[context.arrayElementIndex];
610 else if (additionalItemsSchema_)
611 context.valueSchema = additionalItemsSchema_;
612 else if (additionalItems_)
613 context.valueSchema = GetTypeless();
615 RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
618 context.valueSchema = GetTypeless();
620 context.arrayElementIndex++;
625 RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
626 if (context.patternPropertiesValidatorCount > 0) {
627 bool otherValid = false;
628 SizeType count = context.patternPropertiesValidatorCount;
629 if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
630 otherValid = context.patternPropertiesValidators[--count]->IsValid();
632 bool patternValid = true;
633 for (SizeType i = 0; i < count; i++)
634 if (!context.patternPropertiesValidators[i]->IsValid()) {
635 patternValid = false;
639 if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
641 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
643 else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
644 if (!patternValid || !otherValid)
645 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
647 else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
648 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
652 const uint64_t h = context.factory.GetHashCode(context.hasher);
653 for (SizeType i = 0; i < enumCount_; i++)
656 RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
661 for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
662 if (!context.validators[i]->IsValid())
663 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
665 if (anyOf_.schemas) {
666 for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
667 if (context.validators[i]->IsValid())
669 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
673 if (oneOf_.schemas) {
674 bool oneValid = false;
675 for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
676 if (context.validators[i]->IsValid()) {
678 RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
683 RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
686 if (not_ && context.validators[notValidatorIndex_]->IsValid())
687 RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
692 bool Null(Context& context) const {
693 if (!(type_ & (1 << kNullSchemaType)))
694 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
695 return CreateParallelValidator(context);
698 bool Bool(Context& context, bool) const {
699 if (!(type_ & (1 << kBooleanSchemaType)))
700 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
701 return CreateParallelValidator(context);
704 bool Int(Context& context, int i) const {
705 if (!CheckInt(context, i))
707 return CreateParallelValidator(context);
710 bool Uint(Context& context, unsigned u) const {
711 if (!CheckUint(context, u))
713 return CreateParallelValidator(context);
716 bool Int64(Context& context, int64_t i) const {
717 if (!CheckInt(context, i))
719 return CreateParallelValidator(context);
722 bool Uint64(Context& context, uint64_t u) const {
723 if (!CheckUint(context, u))
725 return CreateParallelValidator(context);
728 bool Double(Context& context, double d) const {
729 if (!(type_ & (1 << kNumberSchemaType)))
730 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
732 if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
735 if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
738 if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
741 return CreateParallelValidator(context);
744 bool String(Context& context, const Ch* str, SizeType length, bool) const {
745 if (!(type_ & (1 << kStringSchemaType)))
746 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
748 if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
750 if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
751 if (count < minLength_)
752 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
753 if (count > maxLength_)
754 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
758 if (pattern_ && !IsPatternMatch(pattern_, str, length))
759 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
761 return CreateParallelValidator(context);
764 bool StartObject(Context& context) const {
765 if (!(type_ & (1 << kObjectSchemaType)))
766 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
768 if (hasDependencies_ || hasRequired_) {
769 context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
770 std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
773 if (patternProperties_) { // pre-allocate schema array
774 SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
775 context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
776 context.patternPropertiesSchemaCount = 0;
777 std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
780 return CreateParallelValidator(context);
783 bool Key(Context& context, const Ch* str, SizeType len, bool) const {
784 if (patternProperties_) {
785 context.patternPropertiesSchemaCount = 0;
786 for (SizeType i = 0; i < patternPropertyCount_; i++)
787 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
788 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
792 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
793 if (context.patternPropertiesSchemaCount > 0) {
794 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
795 context.valueSchema = GetTypeless();
796 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
799 context.valueSchema = properties_[index].schema;
801 if (context.propertyExist)
802 context.propertyExist[index] = true;
807 if (additionalPropertiesSchema_) {
808 if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
809 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
810 context.valueSchema = GetTypeless();
811 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
814 context.valueSchema = additionalPropertiesSchema_;
817 else if (additionalProperties_) {
818 context.valueSchema = GetTypeless();
822 if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
823 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
828 bool EndObject(Context& context, SizeType memberCount) const {
830 for (SizeType index = 0; index < propertyCount_; index++)
831 if (properties_[index].required)
832 if (!context.propertyExist[index])
833 RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
835 if (memberCount < minProperties_)
836 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
838 if (memberCount > maxProperties_)
839 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
841 if (hasDependencies_) {
842 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
843 if (context.propertyExist[sourceIndex]) {
844 if (properties_[sourceIndex].dependencies) {
845 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
846 if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
847 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
849 else if (properties_[sourceIndex].dependenciesSchema)
850 if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
851 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
858 bool StartArray(Context& context) const {
859 if (!(type_ & (1 << kArraySchemaType)))
860 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
862 context.arrayElementIndex = 0;
863 context.inArray = true;
865 return CreateParallelValidator(context);
868 bool EndArray(Context& context, SizeType elementCount) const {
869 context.inArray = false;
871 if (elementCount < minItems_)
872 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
874 if (elementCount > maxItems_)
875 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
880 // Generate functions for string literal according to Ch
881 #define RAPIDJSON_STRING_(name, ...) \
882 static const ValueType& Get##name##String() {\
883 static const Ch s[] = { __VA_ARGS__, '\0' };\
884 static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
888 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
889 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
890 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
891 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
892 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
893 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
894 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
895 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
896 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
897 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
898 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
899 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
900 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
901 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
902 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
903 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
904 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
905 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
906 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
907 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
908 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
909 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
910 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
911 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
912 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
913 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
914 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
915 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
916 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
917 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
918 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
919 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
920 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
922 #undef RAPIDJSON_STRING_
925 enum SchemaValueType {
936 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
937 typedef internal::GenericRegex<EncodingType> RegexType;
938 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
939 typedef std::basic_regex<Ch> RegexType;
941 typedef char RegexType;
945 SchemaArray() : schemas(), count() {}
946 ~SchemaArray() { AllocatorType::Free(schemas); }
947 const SchemaType** schemas;
948 SizeType begin; // begin index of context.validators
952 static const SchemaType* GetTypeless() {
953 static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
957 template <typename V1, typename V2>
958 void AddUniqueElement(V1& a, const V2& v) {
959 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
962 V1 c(v, *allocator_);
963 a.PushBack(c, *allocator_);
966 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
967 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
968 return itr != value.MemberEnd() ? &(itr->value) : 0;
971 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
972 if (const ValueType* v = GetMember(value, name))
977 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
978 if (const ValueType* v = GetMember(value, name))
979 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
980 out = static_cast<SizeType>(v->GetUint64());
983 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
984 if (const ValueType* v = GetMember(value, name)) {
985 if (v->IsArray() && v->Size() > 0) {
986 PointerType q = p.Append(name, allocator_);
987 out.count = v->Size();
988 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
989 memset(out.schemas, 0, sizeof(Schema*)* out.count);
990 for (SizeType i = 0; i < out.count; i++)
991 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
992 out.begin = validatorCount_;
993 validatorCount_ += out.count;
998 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
999 template <typename ValueType>
1000 RegexType* CreatePattern(const ValueType& value) {
1001 if (value.IsString()) {
1002 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
1003 if (!r->IsValid()) {
1005 AllocatorType::Free(r);
1013 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1014 return pattern->Search(str);
1016 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1017 template <typename ValueType>
1018 RegexType* CreatePattern(const ValueType& value) {
1019 if (value.IsString())
1021 return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1023 catch (const std::regex_error&) {
1028 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1029 std::match_results<const Ch*> r;
1030 return std::regex_search(str, str + length, r, *pattern);
1033 template <typename ValueType>
1034 RegexType* CreatePattern(const ValueType&) { return 0; }
1036 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1037 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1039 void AddType(const ValueType& type) {
1040 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1041 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1042 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1043 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1044 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1045 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1046 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1049 bool CreateParallelValidator(Context& context) const {
1050 if (enum_ || context.arrayUniqueness)
1051 context.hasher = context.factory.CreateHasher();
1053 if (validatorCount_) {
1054 RAPIDJSON_ASSERT(context.validators == 0);
1055 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1056 context.validatorCount = validatorCount_;
1059 CreateSchemaValidators(context, allOf_);
1062 CreateSchemaValidators(context, anyOf_);
1065 CreateSchemaValidators(context, oneOf_);
1068 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1070 if (hasSchemaDependencies_) {
1071 for (SizeType i = 0; i < propertyCount_; i++)
1072 if (properties_[i].dependenciesSchema)
1073 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1080 void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1081 for (SizeType i = 0; i < schemas.count; i++)
1082 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1086 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1087 SizeType len = name.GetStringLength();
1088 const Ch* str = name.GetString();
1089 for (SizeType index = 0; index < propertyCount_; index++)
1090 if (properties_[index].name.GetStringLength() == len &&
1091 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1099 bool CheckInt(Context& context, int64_t i) const {
1100 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1101 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1103 if (!minimum_.IsNull()) {
1104 if (minimum_.IsInt64()) {
1105 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
1106 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1108 else if (minimum_.IsUint64()) {
1109 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1111 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1115 if (!maximum_.IsNull()) {
1116 if (maximum_.IsInt64()) {
1117 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
1118 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1120 else if (maximum_.IsUint64())
1121 /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64()
1122 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1126 if (!multipleOf_.IsNull()) {
1127 if (multipleOf_.IsUint64()) {
1128 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
1129 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1131 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1138 bool CheckUint(Context& context, uint64_t i) const {
1139 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1140 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1142 if (!minimum_.IsNull()) {
1143 if (minimum_.IsUint64()) {
1144 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
1145 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1147 else if (minimum_.IsInt64())
1148 /* do nothing */; // i >= 0 > minimum.Getint64()
1149 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1153 if (!maximum_.IsNull()) {
1154 if (maximum_.IsUint64()) {
1155 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
1156 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1158 else if (maximum_.IsInt64())
1159 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1160 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1164 if (!multipleOf_.IsNull()) {
1165 if (multipleOf_.IsUint64()) {
1166 if (i % multipleOf_.GetUint64() != 0)
1167 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1169 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1176 bool CheckDoubleMinimum(Context& context, double d) const {
1177 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
1178 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1182 bool CheckDoubleMaximum(Context& context, double d) const {
1183 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
1184 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1188 bool CheckDoubleMultipleOf(Context& context, double d) const {
1189 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1190 double q = std::floor(a / b);
1191 double r = a - q * b;
1193 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1198 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
1199 ~Property() { AllocatorType::Free(dependencies); }
1201 const SchemaType* schema;
1202 const SchemaType* dependenciesSchema;
1203 SizeType dependenciesValidatorIndex;
1208 struct PatternProperty {
1209 PatternProperty() : schema(), pattern() {}
1210 ~PatternProperty() {
1212 pattern->~RegexType();
1213 AllocatorType::Free(pattern);
1216 const SchemaType* schema;
1220 AllocatorType* allocator_;
1222 SizeType enumCount_;
1226 const SchemaType* not_;
1227 unsigned type_; // bitmask of kSchemaType
1228 SizeType validatorCount_;
1229 SizeType notValidatorIndex_;
1231 Property* properties_;
1232 const SchemaType* additionalPropertiesSchema_;
1233 PatternProperty* patternProperties_;
1234 SizeType patternPropertyCount_;
1235 SizeType propertyCount_;
1236 SizeType minProperties_;
1237 SizeType maxProperties_;
1238 bool additionalProperties_;
1239 bool hasDependencies_;
1241 bool hasSchemaDependencies_;
1243 const SchemaType* additionalItemsSchema_;
1244 const SchemaType* itemsList_;
1245 const SchemaType** itemsTuple_;
1246 SizeType itemsTupleCount_;
1249 bool additionalItems_;
1252 RegexType* pattern_;
1253 SizeType minLength_;
1254 SizeType maxLength_;
1259 bool exclusiveMinimum_;
1260 bool exclusiveMaximum_;
1263 template<typename Stack, typename Ch>
1264 struct TokenHelper {
1265 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1266 *documentStack.template Push<Ch>() = '/';
1268 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1269 for (size_t i = 0; i < length; i++)
1270 *documentStack.template Push<Ch>() = buffer[i];
1274 // Partial specialized version for char to prevent buffer copying.
1275 template <typename Stack>
1276 struct TokenHelper<Stack, char> {
1277 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1278 if (sizeof(SizeType) == 4) {
1279 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1281 const char* end = internal::u32toa(index, buffer);
1282 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1285 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1287 const char* end = internal::u64toa(index, buffer);
1288 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1293 } // namespace internal
1295 ///////////////////////////////////////////////////////////////////////////////
1296 // IGenericRemoteSchemaDocumentProvider
1298 template <typename SchemaDocumentType>
1299 class IGenericRemoteSchemaDocumentProvider {
1301 typedef typename SchemaDocumentType::Ch Ch;
1303 virtual ~IGenericRemoteSchemaDocumentProvider() {}
1304 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1307 ///////////////////////////////////////////////////////////////////////////////
1308 // GenericSchemaDocument
1310 //! JSON schema document.
1312 A JSON schema document is a compiled version of a JSON schema.
1313 It is basically a tree of internal::Schema.
1315 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1316 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1317 \tparam Allocator Allocator type for allocating memory of this document.
1319 template <typename ValueT, typename Allocator = CrtAllocator>
1320 class GenericSchemaDocument {
1322 typedef ValueT ValueType;
1323 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1324 typedef Allocator AllocatorType;
1325 typedef typename ValueType::EncodingType EncodingType;
1326 typedef typename EncodingType::Ch Ch;
1327 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1328 typedef GenericPointer<ValueType, Allocator> PointerType;
1329 friend class internal::Schema<GenericSchemaDocument>;
1330 template <typename, typename, typename>
1331 friend class GenericSchemaValidator;
1335 Compile a JSON document into schema document.
1337 \param document A JSON document as source.
1338 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1339 \param allocator An optional allocator instance for allocating memory. Can be null.
1341 explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1342 remoteProvider_(remoteProvider),
1343 allocator_(allocator),
1346 schemaMap_(allocator, kInitialSchemaMapSize),
1347 schemaRef_(allocator, kInitialSchemaRefSize)
1350 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
1352 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1353 // And call AddRefSchema() if there are $ref.
1354 CreateSchemaRecursive(&root_, PointerType(), document, document);
1357 while (!schemaRef_.Empty()) {
1358 SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1359 if (const SchemaType* s = GetSchema(refEntry->target)) {
1360 if (refEntry->schema)
1361 *refEntry->schema = s;
1363 // Create entry in map if not exist
1364 if (!GetSchema(refEntry->source)) {
1365 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1368 refEntry->~SchemaRefEntry();
1371 RAPIDJSON_ASSERT(root_ != 0);
1373 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1376 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1377 //! Move constructor in C++11
1378 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1379 remoteProvider_(rhs.remoteProvider_),
1380 allocator_(rhs.allocator_),
1381 ownAllocator_(rhs.ownAllocator_),
1383 schemaMap_(std::move(rhs.schemaMap_)),
1384 schemaRef_(std::move(rhs.schemaRef_))
1386 rhs.remoteProvider_ = 0;
1388 rhs.ownAllocator_ = 0;
1393 ~GenericSchemaDocument() {
1394 while (!schemaMap_.Empty())
1395 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1397 RAPIDJSON_DELETE(ownAllocator_);
1400 //! Get the root schema.
1401 const SchemaType& GetRoot() const { return *root_; }
1404 //! Prohibit copying
1405 GenericSchemaDocument(const GenericSchemaDocument&);
1406 //! Prohibit assignment
1407 GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1409 struct SchemaRefEntry {
1410 SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1413 const SchemaType** schema;
1416 struct SchemaEntry {
1417 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1420 schema->~SchemaType();
1421 Allocator::Free(schema);
1424 PointerType pointer;
1429 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1431 *schema = SchemaType::GetTypeless();
1433 if (v.GetType() == kObjectType) {
1434 const SchemaType* s = GetSchema(pointer);
1436 CreateSchema(schema, pointer, v, document);
1438 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1439 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1441 else if (v.GetType() == kArrayType)
1442 for (SizeType i = 0; i < v.Size(); i++)
1443 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1446 void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1447 RAPIDJSON_ASSERT(pointer.IsValid());
1449 if (!HandleRefSchema(pointer, schema, v, document)) {
1450 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1451 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1458 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1459 static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1460 static const ValueType kRefValue(kRefString, 4);
1462 typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1463 if (itr == v.MemberEnd())
1466 if (itr->value.IsString()) {
1467 SizeType len = itr->value.GetStringLength();
1469 const Ch* s = itr->value.GetString();
1471 while (i < len && s[i] != '#') // Find the first #
1474 if (i > 0) { // Remote reference, resolve immediately
1475 if (remoteProvider_) {
1476 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
1477 PointerType pointer(&s[i], len - i, allocator_);
1478 if (pointer.IsValid()) {
1479 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1488 else if (s[i] == '#') { // Local reference, defer resolution
1489 PointerType pointer(&s[i], len - i, allocator_);
1490 if (pointer.IsValid()) {
1491 if (const ValueType* nv = pointer.Get(document))
1492 if (HandleRefSchema(source, schema, *nv, document))
1495 new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1504 const SchemaType* GetSchema(const PointerType& pointer) const {
1505 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1506 if (pointer == target->pointer)
1507 return target->schema;
1511 PointerType GetPointer(const SchemaType* schema) const {
1512 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1513 if (schema == target->schema)
1514 return target->pointer;
1515 return PointerType();
1518 static const size_t kInitialSchemaMapSize = 64;
1519 static const size_t kInitialSchemaRefSize = 64;
1521 IRemoteSchemaDocumentProviderType* remoteProvider_;
1522 Allocator *allocator_;
1523 Allocator *ownAllocator_;
1524 const SchemaType* root_; //!< Root schema.
1525 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1526 internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1529 //! GenericSchemaDocument using Value type.
1530 typedef GenericSchemaDocument<Value> SchemaDocument;
1531 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1532 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1534 ///////////////////////////////////////////////////////////////////////////////
1535 // GenericSchemaValidator
1537 //! JSON Schema Validator.
1539 A SAX style JSON schema validator.
1540 It uses a \c GenericSchemaDocument to validate SAX events.
1541 It delegates the incoming SAX events to an output handler.
1542 The default output handler does nothing.
1543 It can be reused multiple times by calling \c Reset().
1545 \tparam SchemaDocumentType Type of schema document.
1546 \tparam OutputHandler Type of output handler. Default handler does nothing.
1547 \tparam StateAllocator Allocator for storing the internal validation states.
1550 typename SchemaDocumentType,
1551 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1552 typename StateAllocator = CrtAllocator>
1553 class GenericSchemaValidator :
1554 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1555 public internal::ISchemaValidator
1558 typedef typename SchemaDocumentType::SchemaType SchemaType;
1559 typedef typename SchemaDocumentType::PointerType PointerType;
1560 typedef typename SchemaType::EncodingType EncodingType;
1561 typedef typename EncodingType::Ch Ch;
1563 //! Constructor without output handler.
1565 \param schemaDocument The schema document to conform to.
1566 \param allocator Optional allocator for storing internal validation states.
1567 \param schemaStackCapacity Optional initial capacity of schema path stack.
1568 \param documentStackCapacity Optional initial capacity of document path stack.
1570 GenericSchemaValidator(
1571 const SchemaDocumentType& schemaDocument,
1572 StateAllocator* allocator = 0,
1573 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1574 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1576 schemaDocument_(&schemaDocument),
1577 root_(schemaDocument.GetRoot()),
1578 outputHandler_(GetNullHandler()),
1579 stateAllocator_(allocator),
1580 ownStateAllocator_(0),
1581 schemaStack_(allocator, schemaStackCapacity),
1582 documentStack_(allocator, documentStackCapacity),
1584 #if RAPIDJSON_SCHEMA_VERBOSE
1590 //! Constructor with output handler.
1592 \param schemaDocument The schema document to conform to.
1593 \param allocator Optional allocator for storing internal validation states.
1594 \param schemaStackCapacity Optional initial capacity of schema path stack.
1595 \param documentStackCapacity Optional initial capacity of document path stack.
1597 GenericSchemaValidator(
1598 const SchemaDocumentType& schemaDocument,
1599 OutputHandler& outputHandler,
1600 StateAllocator* allocator = 0,
1601 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1602 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1604 schemaDocument_(&schemaDocument),
1605 root_(schemaDocument.GetRoot()),
1606 outputHandler_(outputHandler),
1607 stateAllocator_(allocator),
1608 ownStateAllocator_(0),
1609 schemaStack_(allocator, schemaStackCapacity),
1610 documentStack_(allocator, documentStackCapacity),
1612 #if RAPIDJSON_SCHEMA_VERBOSE
1619 ~GenericSchemaValidator() {
1621 RAPIDJSON_DELETE(ownStateAllocator_);
1624 //! Reset the internal states.
1626 while (!schemaStack_.Empty())
1628 documentStack_.Clear();
1632 //! Checks whether the current state is valid.
1633 // Implementation of ISchemaValidator
1634 virtual bool IsValid() const { return valid_; }
1636 //! Gets the JSON pointer pointed to the invalid schema.
1637 PointerType GetInvalidSchemaPointer() const {
1638 return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
1641 //! Gets the keyword of invalid schema.
1642 const Ch* GetInvalidSchemaKeyword() const {
1643 return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1646 //! Gets the JSON pointer pointed to the invalid value.
1647 PointerType GetInvalidDocumentPointer() const {
1648 return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1651 #if RAPIDJSON_SCHEMA_VERBOSE
1652 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
1653 RAPIDJSON_MULTILINEMACRO_BEGIN\
1654 *documentStack_.template Push<Ch>() = '\0';\
1655 documentStack_.template Pop<Ch>(1);\
1656 internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
1657 RAPIDJSON_MULTILINEMACRO_END
1659 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
1662 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
1663 if (!valid_) return false; \
1664 if (!BeginValue() || !CurrentSchema().method arg1) {\
1665 RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
1666 return valid_ = false;\
1669 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
1670 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
1671 if (context->hasher)\
1672 static_cast<HasherType*>(context->hasher)->method arg2;\
1673 if (context->validators)\
1674 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
1675 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
1676 if (context->patternPropertiesValidators)\
1677 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
1678 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
1681 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
1682 return valid_ = EndValue() && outputHandler_.method arg2
1684 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
1685 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
1686 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
1687 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
1689 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
1690 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
1691 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
1692 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
1693 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
1694 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
1695 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
1696 bool RawNumber(const Ch* str, SizeType length, bool copy)
1697 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1698 bool String(const Ch* str, SizeType length, bool copy)
1699 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1701 bool StartObject() {
1702 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
1703 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
1704 return valid_ = outputHandler_.StartObject();
1707 bool Key(const Ch* str, SizeType len, bool copy) {
1708 if (!valid_) return false;
1709 AppendToken(str, len);
1710 if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
1711 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
1712 return valid_ = outputHandler_.Key(str, len, copy);
1715 bool EndObject(SizeType memberCount) {
1716 if (!valid_) return false;
1717 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
1718 if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
1719 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
1723 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
1724 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
1725 return valid_ = outputHandler_.StartArray();
1728 bool EndArray(SizeType elementCount) {
1729 if (!valid_) return false;
1730 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
1731 if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
1732 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
1735 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
1736 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
1737 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
1738 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
1740 // Implementation of ISchemaStateFactory<SchemaType>
1741 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
1742 return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
1743 #if RAPIDJSON_SCHEMA_VERBOSE
1746 &GetStateAllocator());
1749 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
1750 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
1751 v->~GenericSchemaValidator();
1752 StateAllocator::Free(v);
1755 virtual void* CreateHasher() {
1756 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
1759 virtual uint64_t GetHashCode(void* hasher) {
1760 return static_cast<HasherType*>(hasher)->GetHashCode();
1763 virtual void DestroryHasher(void* hasher) {
1764 HasherType* h = static_cast<HasherType*>(hasher);
1766 StateAllocator::Free(h);
1769 virtual void* MallocState(size_t size) {
1770 return GetStateAllocator().Malloc(size);
1773 virtual void FreeState(void* p) {
1774 return StateAllocator::Free(p);
1778 typedef typename SchemaType::Context Context;
1779 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
1780 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
1782 GenericSchemaValidator(
1783 const SchemaDocumentType& schemaDocument,
1784 const SchemaType& root,
1785 #if RAPIDJSON_SCHEMA_VERBOSE
1788 StateAllocator* allocator = 0,
1789 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1790 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1792 schemaDocument_(&schemaDocument),
1794 outputHandler_(GetNullHandler()),
1795 stateAllocator_(allocator),
1796 ownStateAllocator_(0),
1797 schemaStack_(allocator, schemaStackCapacity),
1798 documentStack_(allocator, documentStackCapacity),
1800 #if RAPIDJSON_SCHEMA_VERBOSE
1806 StateAllocator& GetStateAllocator() {
1807 if (!stateAllocator_)
1808 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
1809 return *stateAllocator_;
1813 if (schemaStack_.Empty())
1816 if (CurrentContext().inArray)
1817 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
1819 if (!CurrentSchema().BeginValue(CurrentContext()))
1822 SizeType count = CurrentContext().patternPropertiesSchemaCount;
1823 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
1824 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
1825 bool valueUniqueness = CurrentContext().valueUniqueness;
1826 if (CurrentContext().valueSchema)
1827 PushSchema(*CurrentContext().valueSchema);
1830 CurrentContext().objectPatternValidatorType = patternValidatorType;
1831 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
1832 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
1833 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
1834 for (SizeType i = 0; i < count; i++)
1835 va[validatorCount++] = CreateSchemaValidator(*sa[i]);
1838 CurrentContext().arrayUniqueness = valueUniqueness;
1844 if (!CurrentSchema().EndValue(CurrentContext()))
1847 #if RAPIDJSON_SCHEMA_VERBOSE
1848 GenericStringBuffer<EncodingType> sb;
1849 schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
1851 *documentStack_.template Push<Ch>() = '\0';
1852 documentStack_.template Pop<Ch>(1);
1853 internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
1856 uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
1860 if (!schemaStack_.Empty()) {
1861 Context& context = CurrentContext();
1862 if (context.valueUniqueness) {
1863 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
1865 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
1866 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
1867 if (itr->GetUint64() == h)
1868 RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
1869 a->PushBack(h, GetStateAllocator());
1873 // Remove the last token of document pointer
1874 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
1880 void AppendToken(const Ch* str, SizeType len) {
1881 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
1882 *documentStack_.template PushUnsafe<Ch>() = '/';
1883 for (SizeType i = 0; i < len; i++) {
1884 if (str[i] == '~') {
1885 *documentStack_.template PushUnsafe<Ch>() = '~';
1886 *documentStack_.template PushUnsafe<Ch>() = '0';
1888 else if (str[i] == '/') {
1889 *documentStack_.template PushUnsafe<Ch>() = '~';
1890 *documentStack_.template PushUnsafe<Ch>() = '1';
1893 *documentStack_.template PushUnsafe<Ch>() = str[i];
1897 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
1899 RAPIDJSON_FORCEINLINE void PopSchema() {
1900 Context* c = schemaStack_.template Pop<Context>(1);
1901 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
1902 a->~HashCodeArray();
1903 StateAllocator::Free(a);
1908 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
1909 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
1910 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
1912 static OutputHandler& GetNullHandler() {
1913 static OutputHandler nullHandler;
1917 static const size_t kDefaultSchemaStackCapacity = 1024;
1918 static const size_t kDefaultDocumentStackCapacity = 256;
1919 const SchemaDocumentType* schemaDocument_;
1920 const SchemaType& root_;
1921 OutputHandler& outputHandler_;
1922 StateAllocator* stateAllocator_;
1923 StateAllocator* ownStateAllocator_;
1924 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
1925 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
1927 #if RAPIDJSON_SCHEMA_VERBOSE
1932 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
1934 ///////////////////////////////////////////////////////////////////////////////
1935 // SchemaValidatingReader
1937 //! A helper class for parsing with validation.
1939 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
1941 \tparam parseFlags Combination of \ref ParseFlag.
1942 \tparam InputStream Type of input stream, implementing Stream concept.
1943 \tparam SourceEncoding Encoding of the input stream.
1944 \tparam SchemaDocumentType Type of schema document.
1945 \tparam StackAllocator Allocator type for stack.
1948 unsigned parseFlags,
1949 typename InputStream,
1950 typename SourceEncoding,
1951 typename SchemaDocumentType = SchemaDocument,
1952 typename StackAllocator = CrtAllocator>
1953 class SchemaValidatingReader {
1955 typedef typename SchemaDocumentType::PointerType PointerType;
1956 typedef typename InputStream::Ch Ch;
1960 \param is Input stream.
1961 \param sd Schema document.
1963 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
1965 template <typename Handler>
1966 bool operator()(Handler& handler) {
1967 GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
1968 GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
1969 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
1971 isValid_ = validator.IsValid();
1973 invalidSchemaPointer_ = PointerType();
1974 invalidSchemaKeyword_ = 0;
1975 invalidDocumentPointer_ = PointerType();
1978 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
1979 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
1980 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
1983 return parseResult_;
1986 const ParseResult& GetParseResult() const { return parseResult_; }
1987 bool IsValid() const { return isValid_; }
1988 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
1989 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
1990 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
1994 const SchemaDocumentType& sd_;
1996 ParseResult parseResult_;
1997 PointerType invalidSchemaPointer_;
1998 const Ch* invalidSchemaKeyword_;
1999 PointerType invalidDocumentPointer_;
2003 RAPIDJSON_NAMESPACE_END
2006 #endif // RAPIDJSON_SCHEMA_H_