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 "stringbuffer.h"
21 #include <cmath> // abs, floor
23 #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
24 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
26 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
29 #if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
30 #define RAPIDJSON_SCHEMA_USE_STDREGEX 1
32 #define RAPIDJSON_SCHEMA_USE_STDREGEX 0
35 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
36 #include "internal/regex.h"
37 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
41 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
42 #define RAPIDJSON_SCHEMA_HAS_REGEX 1
44 #define RAPIDJSON_SCHEMA_HAS_REGEX 0
47 #ifndef RAPIDJSON_SCHEMA_VERBOSE
48 #define RAPIDJSON_SCHEMA_VERBOSE 0
51 #if RAPIDJSON_SCHEMA_VERBOSE
52 #include "stringbuffer.h"
58 RAPIDJSON_DIAG_OFF(effc++)
62 RAPIDJSON_DIAG_OFF(weak-vtables)
63 RAPIDJSON_DIAG_OFF(exit-time-destructors)
64 RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
65 RAPIDJSON_DIAG_OFF(variadic-macros)
66 #elif defined(_MSC_VER)
67 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
70 RAPIDJSON_NAMESPACE_BEGIN
72 ///////////////////////////////////////////////////////////////////////////////
75 #if RAPIDJSON_SCHEMA_VERBOSE
79 inline void PrintInvalidKeyword(const char* keyword) {
80 printf("Fail keyword: %s\n", keyword);
83 inline void PrintInvalidKeyword(const wchar_t* keyword) {
84 wprintf(L"Fail keyword: %ls\n", keyword);
87 inline void PrintInvalidDocument(const char* document) {
88 printf("Fail document: %s\n\n", document);
91 inline void PrintInvalidDocument(const wchar_t* document) {
92 wprintf(L"Fail document: %ls\n\n", document);
95 inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
96 printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
99 inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
100 wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
103 } // namespace internal
105 #endif // RAPIDJSON_SCHEMA_VERBOSE
107 ///////////////////////////////////////////////////////////////////////////////
108 // RAPIDJSON_INVALID_KEYWORD_RETURN
110 #if RAPIDJSON_SCHEMA_VERBOSE
111 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
113 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
116 #define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
117 RAPIDJSON_MULTILINEMACRO_BEGIN\
118 context.invalidKeyword = keyword.GetString();\
119 RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
121 RAPIDJSON_MULTILINEMACRO_END
123 ///////////////////////////////////////////////////////////////////////////////
124 // Forward declarations
126 template <typename ValueType, typename Allocator>
127 class GenericSchemaDocument;
131 template <typename SchemaDocumentType>
134 ///////////////////////////////////////////////////////////////////////////////
137 class ISchemaValidator {
139 virtual ~ISchemaValidator() {}
140 virtual bool IsValid() const = 0;
143 ///////////////////////////////////////////////////////////////////////////////
144 // ISchemaStateFactory
146 template <typename SchemaType>
147 class ISchemaStateFactory {
149 virtual ~ISchemaStateFactory() {}
150 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
151 virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
152 virtual void* CreateHasher() = 0;
153 virtual uint64_t GetHashCode(void* hasher) = 0;
154 virtual void DestroryHasher(void* hasher) = 0;
155 virtual void* MallocState(size_t size) = 0;
156 virtual void FreeState(void* p) = 0;
159 ///////////////////////////////////////////////////////////////////////////////
160 // IValidationErrorHandler
162 template <typename SchemaType>
163 class IValidationErrorHandler {
165 typedef typename SchemaType::Ch Ch;
166 typedef typename SchemaType::SValue SValue;
168 virtual ~IValidationErrorHandler() {}
170 virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
171 virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
172 virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
173 virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
174 virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
175 virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
176 virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
177 virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
178 virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
180 virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
181 virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
182 virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
184 virtual void DisallowedItem(SizeType index) = 0;
185 virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
186 virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
187 virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
189 virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
190 virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
191 virtual void StartMissingProperties() = 0;
192 virtual void AddMissingProperty(const SValue& name) = 0;
193 virtual bool EndMissingProperties() = 0;
194 virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
195 virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
197 virtual void StartDependencyErrors() = 0;
198 virtual void StartMissingDependentProperties() = 0;
199 virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
200 virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
201 virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
202 virtual bool EndDependencyErrors() = 0;
204 virtual void DisallowedValue() = 0;
205 virtual void StartDisallowedType() = 0;
206 virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
207 virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
208 virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
209 virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
210 virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
211 virtual void Disallowed() = 0;
215 ///////////////////////////////////////////////////////////////////////////////
218 // For comparison of compound value
219 template<typename Encoding, typename Allocator>
222 typedef typename Encoding::Ch Ch;
224 Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
226 bool Null() { return WriteType(kNullType); }
227 bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
228 bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
229 bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
230 bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
231 bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
232 bool Double(double d) {
234 if (d < 0) n.u.i = static_cast<int64_t>(d);
235 else n.u.u = static_cast<uint64_t>(d);
237 return WriteNumber(n);
240 bool RawNumber(const Ch* str, SizeType len, bool) {
241 WriteBuffer(kNumberType, str, len * sizeof(Ch));
245 bool String(const Ch* str, SizeType len, bool) {
246 WriteBuffer(kStringType, str, len * sizeof(Ch));
250 bool StartObject() { return true; }
251 bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
252 bool EndObject(SizeType memberCount) {
253 uint64_t h = Hash(0, kObjectType);
254 uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
255 for (SizeType i = 0; i < memberCount; i++)
256 h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
257 *stack_.template Push<uint64_t>() = h;
261 bool StartArray() { return true; }
262 bool EndArray(SizeType elementCount) {
263 uint64_t h = Hash(0, kArrayType);
264 uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
265 for (SizeType i = 0; i < elementCount; i++)
266 h = Hash(h, e[i]); // Use hash to achieve element order sensitive
267 *stack_.template Push<uint64_t>() = h;
271 bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
273 uint64_t GetHashCode() const {
274 RAPIDJSON_ASSERT(IsValid());
275 return *stack_.template Top<uint64_t>();
279 static const size_t kDefaultSize = 256;
288 bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
290 bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
292 bool WriteBuffer(Type type, const void* data, size_t len) {
293 // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
294 uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
295 const unsigned char* d = static_cast<const unsigned char*>(data);
296 for (size_t i = 0; i < len; i++)
298 *stack_.template Push<uint64_t>() = h;
302 static uint64_t Hash(uint64_t h, uint64_t d) {
303 static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
309 Stack<Allocator> stack_;
312 ///////////////////////////////////////////////////////////////////////////////
313 // SchemaValidationContext
315 template <typename SchemaDocumentType>
316 struct SchemaValidationContext {
317 typedef Schema<SchemaDocumentType> SchemaType;
318 typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
319 typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
320 typedef typename SchemaType::ValueType ValueType;
321 typedef typename ValueType::Ch Ch;
323 enum PatternValidatorType {
324 kPatternValidatorOnly,
325 kPatternValidatorWithProperty,
326 kPatternValidatorWithAdditionalProperty
329 SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) :
336 arrayElementHashCodes(),
339 patternPropertiesValidators(),
340 patternPropertiesValidatorCount(),
341 patternPropertiesSchemas(),
342 patternPropertiesSchemaCount(),
343 valuePatternValidatorType(kPatternValidatorOnly),
346 valueUniqueness(false),
347 arrayUniqueness(false)
351 ~SchemaValidationContext() {
353 factory.DestroryHasher(hasher);
355 for (SizeType i = 0; i < validatorCount; i++)
356 factory.DestroySchemaValidator(validators[i]);
357 factory.FreeState(validators);
359 if (patternPropertiesValidators) {
360 for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
361 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
362 factory.FreeState(patternPropertiesValidators);
364 if (patternPropertiesSchemas)
365 factory.FreeState(patternPropertiesSchemas);
367 factory.FreeState(propertyExist);
370 SchemaValidatorFactoryType& factory;
371 ErrorHandlerType& error_handler;
372 const SchemaType* schema;
373 const SchemaType* valueSchema;
374 const Ch* invalidKeyword;
375 void* hasher; // Only validator access
376 void* arrayElementHashCodes; // Only validator access this
377 ISchemaValidator** validators;
378 SizeType validatorCount;
379 ISchemaValidator** patternPropertiesValidators;
380 SizeType patternPropertiesValidatorCount;
381 const SchemaType** patternPropertiesSchemas;
382 SizeType patternPropertiesSchemaCount;
383 PatternValidatorType valuePatternValidatorType;
384 PatternValidatorType objectPatternValidatorType;
385 SizeType arrayElementIndex;
388 bool valueUniqueness;
389 bool arrayUniqueness;
392 ///////////////////////////////////////////////////////////////////////////////
395 template <typename SchemaDocumentType>
398 typedef typename SchemaDocumentType::ValueType ValueType;
399 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
400 typedef typename SchemaDocumentType::PointerType PointerType;
401 typedef typename ValueType::EncodingType EncodingType;
402 typedef typename EncodingType::Ch Ch;
403 typedef SchemaValidationContext<SchemaDocumentType> Context;
404 typedef Schema<SchemaDocumentType> SchemaType;
405 typedef GenericValue<EncodingType, AllocatorType> SValue;
406 typedef IValidationErrorHandler<Schema> ErrorHandler;
407 friend class GenericSchemaDocument<ValueType, AllocatorType>;
409 Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
410 allocator_(allocator),
411 uri_(schemaDocument->GetURI(), *allocator),
413 typeless_(schemaDocument->GetTypeless()),
417 type_((1 << kTotalSchemaType) - 1), // typeless
419 notValidatorIndex_(),
421 additionalPropertiesSchema_(),
422 patternProperties_(),
423 patternPropertyCount_(),
426 maxProperties_(SizeType(~0)),
427 additionalProperties_(true),
430 hasSchemaDependencies_(),
431 additionalItemsSchema_(),
436 maxItems_(SizeType(~0)),
437 additionalItems_(true),
441 maxLength_(~SizeType(0)),
442 exclusiveMinimum_(false),
443 exclusiveMaximum_(false),
444 defaultValueLength_(0)
446 typedef typename SchemaDocumentType::ValueType ValueType;
447 typedef typename ValueType::ConstValueIterator ConstValueIterator;
448 typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
450 if (!value.IsObject())
453 if (const ValueType* v = GetMember(value, GetTypeString())) {
457 else if (v->IsArray())
458 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
462 if (const ValueType* v = GetMember(value, GetEnumString()))
463 if (v->IsArray() && v->Size() > 0) {
464 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
465 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
466 typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
467 char buffer[256u + 24];
468 MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
469 EnumHasherType h(&hasherAllocator, 256);
471 enum_[enumCount_++] = h.GetHashCode();
475 if (schemaDocument) {
476 AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
477 AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
478 AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
481 if (const ValueType* v = GetMember(value, GetNotString())) {
482 schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document);
483 notValidatorIndex_ = validatorCount_;
489 const ValueType* properties = GetMember(value, GetPropertiesString());
490 const ValueType* required = GetMember(value, GetRequiredString());
491 const ValueType* dependencies = GetMember(value, GetDependenciesString());
493 // Gather properties from properties/required/dependencies
494 SValue allProperties(kArrayType);
496 if (properties && properties->IsObject())
497 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
498 AddUniqueElement(allProperties, itr->name);
500 if (required && required->IsArray())
501 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
503 AddUniqueElement(allProperties, *itr);
505 if (dependencies && dependencies->IsObject())
506 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
507 AddUniqueElement(allProperties, itr->name);
508 if (itr->value.IsArray())
509 for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
511 AddUniqueElement(allProperties, *i);
514 if (allProperties.Size() > 0) {
515 propertyCount_ = allProperties.Size();
516 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
517 for (SizeType i = 0; i < propertyCount_; i++) {
518 new (&properties_[i]) Property();
519 properties_[i].name = allProperties[i];
520 properties_[i].schema = typeless_;
525 if (properties && properties->IsObject()) {
526 PointerType q = p.Append(GetPropertiesString(), allocator_);
527 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
529 if (FindPropertyIndex(itr->name, &index))
530 schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
534 if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
535 PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
536 patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
537 patternPropertyCount_ = 0;
539 for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
540 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
541 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
542 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
543 patternPropertyCount_++;
547 if (required && required->IsArray())
548 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
549 if (itr->IsString()) {
551 if (FindPropertyIndex(*itr, &index)) {
552 properties_[index].required = true;
557 if (dependencies && dependencies->IsObject()) {
558 PointerType q = p.Append(GetDependenciesString(), allocator_);
559 hasDependencies_ = true;
560 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
561 SizeType sourceIndex;
562 if (FindPropertyIndex(itr->name, &sourceIndex)) {
563 if (itr->value.IsArray()) {
564 properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
565 std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
566 for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
567 SizeType targetIndex;
568 if (FindPropertyIndex(*targetItr, &targetIndex))
569 properties_[sourceIndex].dependencies[targetIndex] = true;
572 else if (itr->value.IsObject()) {
573 hasSchemaDependencies_ = true;
574 schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
575 properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
582 if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
584 additionalProperties_ = v->GetBool();
585 else if (v->IsObject())
586 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
589 AssignIfExist(minProperties_, value, GetMinPropertiesString());
590 AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
593 if (const ValueType* v = GetMember(value, GetItemsString())) {
594 PointerType q = p.Append(GetItemsString(), allocator_);
595 if (v->IsObject()) // List validation
596 schemaDocument->CreateSchema(&itemsList_, q, *v, document);
597 else if (v->IsArray()) { // Tuple validation
598 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
600 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
601 schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
605 AssignIfExist(minItems_, value, GetMinItemsString());
606 AssignIfExist(maxItems_, value, GetMaxItemsString());
608 if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
610 additionalItems_ = v->GetBool();
611 else if (v->IsObject())
612 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
615 AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
618 AssignIfExist(minLength_, value, GetMinLengthString());
619 AssignIfExist(maxLength_, value, GetMaxLengthString());
621 if (const ValueType* v = GetMember(value, GetPatternString()))
622 pattern_ = CreatePattern(*v);
625 if (const ValueType* v = GetMember(value, GetMinimumString()))
627 minimum_.CopyFrom(*v, *allocator_);
629 if (const ValueType* v = GetMember(value, GetMaximumString()))
631 maximum_.CopyFrom(*v, *allocator_);
633 AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
634 AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
636 if (const ValueType* v = GetMember(value, GetMultipleOfString()))
637 if (v->IsNumber() && v->GetDouble() > 0.0)
638 multipleOf_.CopyFrom(*v, *allocator_);
641 if (const ValueType* v = GetMember(value, GetDefaultValueString()))
643 defaultValueLength_ = v->GetStringLength();
648 AllocatorType::Free(enum_);
650 for (SizeType i = 0; i < propertyCount_; i++)
651 properties_[i].~Property();
652 AllocatorType::Free(properties_);
654 if (patternProperties_) {
655 for (SizeType i = 0; i < patternPropertyCount_; i++)
656 patternProperties_[i].~PatternProperty();
657 AllocatorType::Free(patternProperties_);
659 AllocatorType::Free(itemsTuple_);
660 #if RAPIDJSON_SCHEMA_HAS_REGEX
662 pattern_->~RegexType();
663 AllocatorType::Free(pattern_);
668 const SValue& GetURI() const {
672 const PointerType& GetPointer() const {
676 bool BeginValue(Context& context) const {
677 if (context.inArray) {
679 context.valueUniqueness = true;
682 context.valueSchema = itemsList_;
683 else if (itemsTuple_) {
684 if (context.arrayElementIndex < itemsTupleCount_)
685 context.valueSchema = itemsTuple_[context.arrayElementIndex];
686 else if (additionalItemsSchema_)
687 context.valueSchema = additionalItemsSchema_;
688 else if (additionalItems_)
689 context.valueSchema = typeless_;
691 context.error_handler.DisallowedItem(context.arrayElementIndex);
692 RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
696 context.valueSchema = typeless_;
698 context.arrayElementIndex++;
703 RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
704 if (context.patternPropertiesValidatorCount > 0) {
705 bool otherValid = false;
706 SizeType count = context.patternPropertiesValidatorCount;
707 if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
708 otherValid = context.patternPropertiesValidators[--count]->IsValid();
710 bool patternValid = true;
711 for (SizeType i = 0; i < count; i++)
712 if (!context.patternPropertiesValidators[i]->IsValid()) {
713 patternValid = false;
717 if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
719 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
720 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
723 else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
724 if (!patternValid || !otherValid) {
725 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
726 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
729 else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
730 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
731 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
736 const uint64_t h = context.factory.GetHashCode(context.hasher);
737 for (SizeType i = 0; i < enumCount_; i++)
740 context.error_handler.DisallowedValue();
741 RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
746 for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
747 if (!context.validators[i]->IsValid()) {
748 context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
749 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
752 if (anyOf_.schemas) {
753 for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
754 if (context.validators[i]->IsValid())
756 context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
757 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
761 if (oneOf_.schemas) {
762 bool oneValid = false;
763 for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
764 if (context.validators[i]->IsValid()) {
766 context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
767 RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
772 context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
773 RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
777 if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
778 context.error_handler.Disallowed();
779 RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
785 bool Null(Context& context) const {
786 if (!(type_ & (1 << kNullSchemaType))) {
787 DisallowedType(context, GetNullString());
788 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
790 return CreateParallelValidator(context);
793 bool Bool(Context& context, bool) const {
794 if (!(type_ & (1 << kBooleanSchemaType))) {
795 DisallowedType(context, GetBooleanString());
796 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
798 return CreateParallelValidator(context);
801 bool Int(Context& context, int i) const {
802 if (!CheckInt(context, i))
804 return CreateParallelValidator(context);
807 bool Uint(Context& context, unsigned u) const {
808 if (!CheckUint(context, u))
810 return CreateParallelValidator(context);
813 bool Int64(Context& context, int64_t i) const {
814 if (!CheckInt(context, i))
816 return CreateParallelValidator(context);
819 bool Uint64(Context& context, uint64_t u) const {
820 if (!CheckUint(context, u))
822 return CreateParallelValidator(context);
825 bool Double(Context& context, double d) const {
826 if (!(type_ & (1 << kNumberSchemaType))) {
827 DisallowedType(context, GetNumberString());
828 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
831 if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
834 if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
837 if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
840 return CreateParallelValidator(context);
843 bool String(Context& context, const Ch* str, SizeType length, bool) const {
844 if (!(type_ & (1 << kStringSchemaType))) {
845 DisallowedType(context, GetStringString());
846 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
849 if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
851 if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
852 if (count < minLength_) {
853 context.error_handler.TooShort(str, length, minLength_);
854 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
856 if (count > maxLength_) {
857 context.error_handler.TooLong(str, length, maxLength_);
858 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
863 if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
864 context.error_handler.DoesNotMatch(str, length);
865 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
868 return CreateParallelValidator(context);
871 bool StartObject(Context& context) const {
872 if (!(type_ & (1 << kObjectSchemaType))) {
873 DisallowedType(context, GetObjectString());
874 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
877 if (hasDependencies_ || hasRequired_) {
878 context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
879 std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
882 if (patternProperties_) { // pre-allocate schema array
883 SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
884 context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
885 context.patternPropertiesSchemaCount = 0;
886 std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
889 return CreateParallelValidator(context);
892 bool Key(Context& context, const Ch* str, SizeType len, bool) const {
893 if (patternProperties_) {
894 context.patternPropertiesSchemaCount = 0;
895 for (SizeType i = 0; i < patternPropertyCount_; i++)
896 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
897 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
898 context.valueSchema = typeless_;
903 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
904 if (context.patternPropertiesSchemaCount > 0) {
905 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
906 context.valueSchema = typeless_;
907 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
910 context.valueSchema = properties_[index].schema;
912 if (context.propertyExist)
913 context.propertyExist[index] = true;
918 if (additionalPropertiesSchema_) {
919 if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
920 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
921 context.valueSchema = typeless_;
922 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
925 context.valueSchema = additionalPropertiesSchema_;
928 else if (additionalProperties_) {
929 context.valueSchema = typeless_;
933 if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
934 context.error_handler.DisallowedProperty(str, len);
935 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
941 bool EndObject(Context& context, SizeType memberCount) const {
943 context.error_handler.StartMissingProperties();
944 for (SizeType index = 0; index < propertyCount_; index++)
945 if (properties_[index].required && !context.propertyExist[index])
946 if (properties_[index].schema->defaultValueLength_ == 0 )
947 context.error_handler.AddMissingProperty(properties_[index].name);
948 if (context.error_handler.EndMissingProperties())
949 RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
952 if (memberCount < minProperties_) {
953 context.error_handler.TooFewProperties(memberCount, minProperties_);
954 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
957 if (memberCount > maxProperties_) {
958 context.error_handler.TooManyProperties(memberCount, maxProperties_);
959 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
962 if (hasDependencies_) {
963 context.error_handler.StartDependencyErrors();
964 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
965 const Property& source = properties_[sourceIndex];
966 if (context.propertyExist[sourceIndex]) {
967 if (source.dependencies) {
968 context.error_handler.StartMissingDependentProperties();
969 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
970 if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
971 context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
972 context.error_handler.EndMissingDependentProperties(source.name);
974 else if (source.dependenciesSchema) {
975 ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
976 if (!dependenciesValidator->IsValid())
977 context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
981 if (context.error_handler.EndDependencyErrors())
982 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
988 bool StartArray(Context& context) const {
989 if (!(type_ & (1 << kArraySchemaType))) {
990 DisallowedType(context, GetArrayString());
991 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
994 context.arrayElementIndex = 0;
995 context.inArray = true;
997 return CreateParallelValidator(context);
1000 bool EndArray(Context& context, SizeType elementCount) const {
1001 context.inArray = false;
1003 if (elementCount < minItems_) {
1004 context.error_handler.TooFewItems(elementCount, minItems_);
1005 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
1008 if (elementCount > maxItems_) {
1009 context.error_handler.TooManyItems(elementCount, maxItems_);
1010 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
1016 // Generate functions for string literal according to Ch
1017 #define RAPIDJSON_STRING_(name, ...) \
1018 static const ValueType& Get##name##String() {\
1019 static const Ch s[] = { __VA_ARGS__, '\0' };\
1020 static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
1024 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
1025 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
1026 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
1027 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
1028 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
1029 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
1030 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
1031 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
1032 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
1033 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
1034 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
1035 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
1036 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
1037 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1038 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
1039 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
1040 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1041 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1042 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1043 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1044 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
1045 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
1046 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
1047 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
1048 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
1049 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
1050 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
1051 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
1052 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
1053 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
1054 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
1055 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
1056 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
1057 RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
1059 #undef RAPIDJSON_STRING_
1062 enum SchemaValueType {
1073 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1074 typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
1075 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1076 typedef std::basic_regex<Ch> RegexType;
1078 typedef char RegexType;
1081 struct SchemaArray {
1082 SchemaArray() : schemas(), count() {}
1083 ~SchemaArray() { AllocatorType::Free(schemas); }
1084 const SchemaType** schemas;
1085 SizeType begin; // begin index of context.validators
1089 template <typename V1, typename V2>
1090 void AddUniqueElement(V1& a, const V2& v) {
1091 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
1094 V1 c(v, *allocator_);
1095 a.PushBack(c, *allocator_);
1098 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
1099 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
1100 return itr != value.MemberEnd() ? &(itr->value) : 0;
1103 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
1104 if (const ValueType* v = GetMember(value, name))
1109 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
1110 if (const ValueType* v = GetMember(value, name))
1111 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
1112 out = static_cast<SizeType>(v->GetUint64());
1115 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
1116 if (const ValueType* v = GetMember(value, name)) {
1117 if (v->IsArray() && v->Size() > 0) {
1118 PointerType q = p.Append(name, allocator_);
1119 out.count = v->Size();
1120 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
1121 memset(out.schemas, 0, sizeof(Schema*)* out.count);
1122 for (SizeType i = 0; i < out.count; i++)
1123 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
1124 out.begin = validatorCount_;
1125 validatorCount_ += out.count;
1130 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1131 template <typename ValueType>
1132 RegexType* CreatePattern(const ValueType& value) {
1133 if (value.IsString()) {
1134 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
1135 if (!r->IsValid()) {
1137 AllocatorType::Free(r);
1145 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1146 GenericRegexSearch<RegexType> rs(*pattern);
1147 return rs.Search(str);
1149 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1150 template <typename ValueType>
1151 RegexType* CreatePattern(const ValueType& value) {
1152 if (value.IsString())
1153 RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
1155 return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1157 catch (const std::regex_error&) {
1158 AllocatorType::Free(r);
1163 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1164 std::match_results<const Ch*> r;
1165 return std::regex_search(str, str + length, r, *pattern);
1168 template <typename ValueType>
1169 RegexType* CreatePattern(const ValueType&) { return 0; }
1171 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1172 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1174 void AddType(const ValueType& type) {
1175 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1176 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1177 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1178 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1179 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1180 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1181 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1184 bool CreateParallelValidator(Context& context) const {
1185 if (enum_ || context.arrayUniqueness)
1186 context.hasher = context.factory.CreateHasher();
1188 if (validatorCount_) {
1189 RAPIDJSON_ASSERT(context.validators == 0);
1190 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1191 context.validatorCount = validatorCount_;
1194 CreateSchemaValidators(context, allOf_);
1197 CreateSchemaValidators(context, anyOf_);
1200 CreateSchemaValidators(context, oneOf_);
1203 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1205 if (hasSchemaDependencies_) {
1206 for (SizeType i = 0; i < propertyCount_; i++)
1207 if (properties_[i].dependenciesSchema)
1208 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1215 void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1216 for (SizeType i = 0; i < schemas.count; i++)
1217 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1221 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1222 SizeType len = name.GetStringLength();
1223 const Ch* str = name.GetString();
1224 for (SizeType index = 0; index < propertyCount_; index++)
1225 if (properties_[index].name.GetStringLength() == len &&
1226 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1234 bool CheckInt(Context& context, int64_t i) const {
1235 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1236 DisallowedType(context, GetIntegerString());
1237 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1240 if (!minimum_.IsNull()) {
1241 if (minimum_.IsInt64()) {
1242 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
1243 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1244 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1247 else if (minimum_.IsUint64()) {
1248 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1249 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1251 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1255 if (!maximum_.IsNull()) {
1256 if (maximum_.IsInt64()) {
1257 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
1258 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1259 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1262 else if (maximum_.IsUint64()) { }
1263 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1264 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1268 if (!multipleOf_.IsNull()) {
1269 if (multipleOf_.IsUint64()) {
1270 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
1271 context.error_handler.NotMultipleOf(i, multipleOf_);
1272 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1275 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1282 bool CheckUint(Context& context, uint64_t i) const {
1283 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1284 DisallowedType(context, GetIntegerString());
1285 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1288 if (!minimum_.IsNull()) {
1289 if (minimum_.IsUint64()) {
1290 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
1291 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1292 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1295 else if (minimum_.IsInt64())
1296 /* do nothing */; // i >= 0 > minimum.Getint64()
1297 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1301 if (!maximum_.IsNull()) {
1302 if (maximum_.IsUint64()) {
1303 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
1304 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1305 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1308 else if (maximum_.IsInt64()) {
1309 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1310 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1312 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1316 if (!multipleOf_.IsNull()) {
1317 if (multipleOf_.IsUint64()) {
1318 if (i % multipleOf_.GetUint64() != 0) {
1319 context.error_handler.NotMultipleOf(i, multipleOf_);
1320 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1323 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1330 bool CheckDoubleMinimum(Context& context, double d) const {
1331 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
1332 context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
1333 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1338 bool CheckDoubleMaximum(Context& context, double d) const {
1339 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
1340 context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
1341 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1346 bool CheckDoubleMultipleOf(Context& context, double d) const {
1347 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1348 double q = std::floor(a / b);
1349 double r = a - q * b;
1351 context.error_handler.NotMultipleOf(d, multipleOf_);
1352 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1357 void DisallowedType(Context& context, const ValueType& actualType) const {
1358 ErrorHandler& eh = context.error_handler;
1359 eh.StartDisallowedType();
1361 if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
1362 if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
1363 if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
1364 if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
1365 if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
1367 if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
1368 else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
1370 eh.EndDisallowedType(actualType);
1374 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
1375 ~Property() { AllocatorType::Free(dependencies); }
1377 const SchemaType* schema;
1378 const SchemaType* dependenciesSchema;
1379 SizeType dependenciesValidatorIndex;
1384 struct PatternProperty {
1385 PatternProperty() : schema(), pattern() {}
1386 ~PatternProperty() {
1388 pattern->~RegexType();
1389 AllocatorType::Free(pattern);
1392 const SchemaType* schema;
1396 AllocatorType* allocator_;
1398 PointerType pointer_;
1399 const SchemaType* typeless_;
1401 SizeType enumCount_;
1405 const SchemaType* not_;
1406 unsigned type_; // bitmask of kSchemaType
1407 SizeType validatorCount_;
1408 SizeType notValidatorIndex_;
1410 Property* properties_;
1411 const SchemaType* additionalPropertiesSchema_;
1412 PatternProperty* patternProperties_;
1413 SizeType patternPropertyCount_;
1414 SizeType propertyCount_;
1415 SizeType minProperties_;
1416 SizeType maxProperties_;
1417 bool additionalProperties_;
1418 bool hasDependencies_;
1420 bool hasSchemaDependencies_;
1422 const SchemaType* additionalItemsSchema_;
1423 const SchemaType* itemsList_;
1424 const SchemaType** itemsTuple_;
1425 SizeType itemsTupleCount_;
1428 bool additionalItems_;
1431 RegexType* pattern_;
1432 SizeType minLength_;
1433 SizeType maxLength_;
1438 bool exclusiveMinimum_;
1439 bool exclusiveMaximum_;
1441 SizeType defaultValueLength_;
1444 template<typename Stack, typename Ch>
1445 struct TokenHelper {
1446 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1447 *documentStack.template Push<Ch>() = '/';
1449 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1450 for (size_t i = 0; i < length; i++)
1451 *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1455 // Partial specialized version for char to prevent buffer copying.
1456 template <typename Stack>
1457 struct TokenHelper<Stack, char> {
1458 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1459 if (sizeof(SizeType) == 4) {
1460 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1462 const char* end = internal::u32toa(index, buffer);
1463 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1466 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1468 const char* end = internal::u64toa(index, buffer);
1469 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1474 } // namespace internal
1476 ///////////////////////////////////////////////////////////////////////////////
1477 // IGenericRemoteSchemaDocumentProvider
1479 template <typename SchemaDocumentType>
1480 class IGenericRemoteSchemaDocumentProvider {
1482 typedef typename SchemaDocumentType::Ch Ch;
1484 virtual ~IGenericRemoteSchemaDocumentProvider() {}
1485 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1488 ///////////////////////////////////////////////////////////////////////////////
1489 // GenericSchemaDocument
1491 //! JSON schema document.
1493 A JSON schema document is a compiled version of a JSON schema.
1494 It is basically a tree of internal::Schema.
1496 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1497 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1498 \tparam Allocator Allocator type for allocating memory of this document.
1500 template <typename ValueT, typename Allocator = CrtAllocator>
1501 class GenericSchemaDocument {
1503 typedef ValueT ValueType;
1504 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1505 typedef Allocator AllocatorType;
1506 typedef typename ValueType::EncodingType EncodingType;
1507 typedef typename EncodingType::Ch Ch;
1508 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1509 typedef GenericPointer<ValueType, Allocator> PointerType;
1510 typedef GenericValue<EncodingType, Allocator> URIType;
1511 friend class internal::Schema<GenericSchemaDocument>;
1512 template <typename, typename, typename>
1513 friend class GenericSchemaValidator;
1517 Compile a JSON document into schema document.
1519 \param document A JSON document as source.
1520 \param uri The base URI of this schema document for purposes of violation reporting.
1521 \param uriLength Length of \c name, in code points.
1522 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1523 \param allocator An optional allocator instance for allocating memory. Can be null.
1525 explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
1526 IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1527 remoteProvider_(remoteProvider),
1528 allocator_(allocator),
1532 schemaMap_(allocator, kInitialSchemaMapSize),
1533 schemaRef_(allocator, kInitialSchemaRefSize)
1536 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1539 uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
1541 typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1542 new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_);
1544 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1545 // And call AddRefSchema() if there are $ref.
1546 CreateSchemaRecursive(&root_, PointerType(), document, document);
1549 while (!schemaRef_.Empty()) {
1550 SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1551 if (const SchemaType* s = GetSchema(refEntry->target)) {
1552 if (refEntry->schema)
1553 *refEntry->schema = s;
1555 // Create entry in map if not exist
1556 if (!GetSchema(refEntry->source)) {
1557 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1560 else if (refEntry->schema)
1561 *refEntry->schema = typeless_;
1563 refEntry->~SchemaRefEntry();
1566 RAPIDJSON_ASSERT(root_ != 0);
1568 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1571 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1572 //! Move constructor in C++11
1573 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1574 remoteProvider_(rhs.remoteProvider_),
1575 allocator_(rhs.allocator_),
1576 ownAllocator_(rhs.ownAllocator_),
1578 typeless_(rhs.typeless_),
1579 schemaMap_(std::move(rhs.schemaMap_)),
1580 schemaRef_(std::move(rhs.schemaRef_)),
1581 uri_(std::move(rhs.uri_))
1583 rhs.remoteProvider_ = 0;
1585 rhs.ownAllocator_ = 0;
1591 ~GenericSchemaDocument() {
1592 while (!schemaMap_.Empty())
1593 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1596 typeless_->~SchemaType();
1597 Allocator::Free(typeless_);
1600 RAPIDJSON_DELETE(ownAllocator_);
1603 const URIType& GetURI() const { return uri_; }
1605 //! Get the root schema.
1606 const SchemaType& GetRoot() const { return *root_; }
1609 //! Prohibit copying
1610 GenericSchemaDocument(const GenericSchemaDocument&);
1611 //! Prohibit assignment
1612 GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1614 struct SchemaRefEntry {
1615 SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1618 const SchemaType** schema;
1621 struct SchemaEntry {
1622 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1625 schema->~SchemaType();
1626 Allocator::Free(schema);
1629 PointerType pointer;
1634 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1636 *schema = typeless_;
1638 if (v.GetType() == kObjectType) {
1639 const SchemaType* s = GetSchema(pointer);
1641 CreateSchema(schema, pointer, v, document);
1643 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1644 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1646 else if (v.GetType() == kArrayType)
1647 for (SizeType i = 0; i < v.Size(); i++)
1648 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1651 void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1652 RAPIDJSON_ASSERT(pointer.IsValid());
1654 if (!HandleRefSchema(pointer, schema, v, document)) {
1655 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1656 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1663 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1664 static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1665 static const ValueType kRefValue(kRefString, 4);
1667 typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1668 if (itr == v.MemberEnd())
1671 if (itr->value.IsString()) {
1672 SizeType len = itr->value.GetStringLength();
1674 const Ch* s = itr->value.GetString();
1676 while (i < len && s[i] != '#') // Find the first #
1679 if (i > 0) { // Remote reference, resolve immediately
1680 if (remoteProvider_) {
1681 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
1682 PointerType pointer(&s[i], len - i, allocator_);
1683 if (pointer.IsValid()) {
1684 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1687 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(source, const_cast<SchemaType*>(sc), false, allocator_);
1694 else if (s[i] == '#') { // Local reference, defer resolution
1695 PointerType pointer(&s[i], len - i, allocator_);
1696 if (pointer.IsValid()) {
1697 if (const ValueType* nv = pointer.Get(document))
1698 if (HandleRefSchema(source, schema, *nv, document))
1701 new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1710 const SchemaType* GetSchema(const PointerType& pointer) const {
1711 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1712 if (pointer == target->pointer)
1713 return target->schema;
1717 PointerType GetPointer(const SchemaType* schema) const {
1718 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1719 if (schema == target->schema)
1720 return target->pointer;
1721 return PointerType();
1724 const SchemaType* GetTypeless() const { return typeless_; }
1726 static const size_t kInitialSchemaMapSize = 64;
1727 static const size_t kInitialSchemaRefSize = 64;
1729 IRemoteSchemaDocumentProviderType* remoteProvider_;
1730 Allocator *allocator_;
1731 Allocator *ownAllocator_;
1732 const SchemaType* root_; //!< Root schema.
1733 SchemaType* typeless_;
1734 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1735 internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1739 //! GenericSchemaDocument using Value type.
1740 typedef GenericSchemaDocument<Value> SchemaDocument;
1741 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1742 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1744 ///////////////////////////////////////////////////////////////////////////////
1745 // GenericSchemaValidator
1747 //! JSON Schema Validator.
1749 A SAX style JSON schema validator.
1750 It uses a \c GenericSchemaDocument to validate SAX events.
1751 It delegates the incoming SAX events to an output handler.
1752 The default output handler does nothing.
1753 It can be reused multiple times by calling \c Reset().
1755 \tparam SchemaDocumentType Type of schema document.
1756 \tparam OutputHandler Type of output handler. Default handler does nothing.
1757 \tparam StateAllocator Allocator for storing the internal validation states.
1760 typename SchemaDocumentType,
1761 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1762 typename StateAllocator = CrtAllocator>
1763 class GenericSchemaValidator :
1764 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1765 public internal::ISchemaValidator,
1766 public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType>
1769 typedef typename SchemaDocumentType::SchemaType SchemaType;
1770 typedef typename SchemaDocumentType::PointerType PointerType;
1771 typedef typename SchemaType::EncodingType EncodingType;
1772 typedef typename SchemaType::SValue SValue;
1773 typedef typename EncodingType::Ch Ch;
1774 typedef GenericStringRef<Ch> StringRefType;
1775 typedef GenericValue<EncodingType, StateAllocator> ValueType;
1777 //! Constructor without output handler.
1779 \param schemaDocument The schema document to conform to.
1780 \param allocator Optional allocator for storing internal validation states.
1781 \param schemaStackCapacity Optional initial capacity of schema path stack.
1782 \param documentStackCapacity Optional initial capacity of document path stack.
1784 GenericSchemaValidator(
1785 const SchemaDocumentType& schemaDocument,
1786 StateAllocator* allocator = 0,
1787 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1788 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1790 schemaDocument_(&schemaDocument),
1791 root_(schemaDocument.GetRoot()),
1792 stateAllocator_(allocator),
1793 ownStateAllocator_(0),
1794 schemaStack_(allocator, schemaStackCapacity),
1795 documentStack_(allocator, documentStackCapacity),
1797 error_(kObjectType),
1799 missingDependents_(),
1801 #if RAPIDJSON_SCHEMA_VERBOSE
1807 //! Constructor with output handler.
1809 \param schemaDocument The schema document to conform to.
1810 \param allocator Optional allocator for storing internal validation states.
1811 \param schemaStackCapacity Optional initial capacity of schema path stack.
1812 \param documentStackCapacity Optional initial capacity of document path stack.
1814 GenericSchemaValidator(
1815 const SchemaDocumentType& schemaDocument,
1816 OutputHandler& outputHandler,
1817 StateAllocator* allocator = 0,
1818 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1819 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1821 schemaDocument_(&schemaDocument),
1822 root_(schemaDocument.GetRoot()),
1823 stateAllocator_(allocator),
1824 ownStateAllocator_(0),
1825 schemaStack_(allocator, schemaStackCapacity),
1826 documentStack_(allocator, documentStackCapacity),
1827 outputHandler_(&outputHandler),
1828 error_(kObjectType),
1830 missingDependents_(),
1832 #if RAPIDJSON_SCHEMA_VERBOSE
1839 ~GenericSchemaValidator() {
1841 RAPIDJSON_DELETE(ownStateAllocator_);
1844 //! Reset the internal states.
1846 while (!schemaStack_.Empty())
1848 documentStack_.Clear();
1850 currentError_.SetNull();
1851 missingDependents_.SetNull();
1855 //! Checks whether the current state is valid.
1856 // Implementation of ISchemaValidator
1857 virtual bool IsValid() const { return valid_; }
1859 //! Gets the error object.
1860 ValueType& GetError() { return error_; }
1861 const ValueType& GetError() const { return error_; }
1863 //! Gets the JSON pointer pointed to the invalid schema.
1864 PointerType GetInvalidSchemaPointer() const {
1865 return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
1868 //! Gets the keyword of invalid schema.
1869 const Ch* GetInvalidSchemaKeyword() const {
1870 return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1873 //! Gets the JSON pointer pointed to the invalid value.
1874 PointerType GetInvalidDocumentPointer() const {
1875 if (documentStack_.Empty()) {
1876 return PointerType();
1879 return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1883 void NotMultipleOf(int64_t actual, const SValue& expected) {
1884 AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
1886 void NotMultipleOf(uint64_t actual, const SValue& expected) {
1887 AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
1889 void NotMultipleOf(double actual, const SValue& expected) {
1890 AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
1892 void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
1893 AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
1894 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
1896 void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
1897 AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
1898 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
1900 void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
1901 AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
1902 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
1904 void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
1905 AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
1906 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
1908 void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
1909 AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
1910 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
1912 void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
1913 AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
1914 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
1917 void TooLong(const Ch* str, SizeType length, SizeType expected) {
1918 AddNumberError(SchemaType::GetMaxLengthString(),
1919 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
1921 void TooShort(const Ch* str, SizeType length, SizeType expected) {
1922 AddNumberError(SchemaType::GetMinLengthString(),
1923 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
1925 void DoesNotMatch(const Ch* str, SizeType length) {
1926 currentError_.SetObject();
1927 currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
1928 AddCurrentError(SchemaType::GetPatternString());
1931 void DisallowedItem(SizeType index) {
1932 currentError_.SetObject();
1933 currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
1934 AddCurrentError(SchemaType::GetAdditionalItemsString(), true);
1936 void TooFewItems(SizeType actualCount, SizeType expectedCount) {
1937 AddNumberError(SchemaType::GetMinItemsString(),
1938 ValueType(actualCount).Move(), SValue(expectedCount).Move());
1940 void TooManyItems(SizeType actualCount, SizeType expectedCount) {
1941 AddNumberError(SchemaType::GetMaxItemsString(),
1942 ValueType(actualCount).Move(), SValue(expectedCount).Move());
1944 void DuplicateItems(SizeType index1, SizeType index2) {
1945 ValueType duplicates(kArrayType);
1946 duplicates.PushBack(index1, GetStateAllocator());
1947 duplicates.PushBack(index2, GetStateAllocator());
1948 currentError_.SetObject();
1949 currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
1950 AddCurrentError(SchemaType::GetUniqueItemsString(), true);
1953 void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
1954 AddNumberError(SchemaType::GetMaxPropertiesString(),
1955 ValueType(actualCount).Move(), SValue(expectedCount).Move());
1957 void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
1958 AddNumberError(SchemaType::GetMinPropertiesString(),
1959 ValueType(actualCount).Move(), SValue(expectedCount).Move());
1961 void StartMissingProperties() {
1962 currentError_.SetArray();
1964 void AddMissingProperty(const SValue& name) {
1965 currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
1967 bool EndMissingProperties() {
1968 if (currentError_.Empty())
1970 ValueType error(kObjectType);
1971 error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
1972 currentError_ = error;
1973 AddCurrentError(SchemaType::GetRequiredString());
1976 void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
1977 for (SizeType i = 0; i < count; ++i)
1978 MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
1980 void DisallowedProperty(const Ch* name, SizeType length) {
1981 currentError_.SetObject();
1982 currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
1983 AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true);
1986 void StartDependencyErrors() {
1987 currentError_.SetObject();
1989 void StartMissingDependentProperties() {
1990 missingDependents_.SetArray();
1992 void AddMissingDependentProperty(const SValue& targetName) {
1993 missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
1995 void EndMissingDependentProperties(const SValue& sourceName) {
1996 if (!missingDependents_.Empty())
1997 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
1998 missingDependents_, GetStateAllocator());
2000 void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
2001 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
2002 static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
2004 bool EndDependencyErrors() {
2005 if (currentError_.ObjectEmpty())
2007 ValueType error(kObjectType);
2008 error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
2009 currentError_ = error;
2010 AddCurrentError(SchemaType::GetDependenciesString());
2014 void DisallowedValue() {
2015 currentError_.SetObject();
2016 AddCurrentError(SchemaType::GetEnumString());
2018 void StartDisallowedType() {
2019 currentError_.SetArray();
2021 void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
2022 currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
2024 void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
2025 ValueType error(kObjectType);
2026 error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
2027 error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
2028 currentError_ = error;
2029 AddCurrentError(SchemaType::GetTypeString());
2031 void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
2032 for (SizeType i = 0; i < count; ++i) {
2033 MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2036 void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
2037 AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count);
2039 void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
2040 AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count);
2043 currentError_.SetObject();
2044 AddCurrentError(SchemaType::GetNotString());
2047 #define RAPIDJSON_STRING_(name, ...) \
2048 static const StringRefType& Get##name##String() {\
2049 static const Ch s[] = { __VA_ARGS__, '\0' };\
2050 static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
2054 RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
2055 RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
2056 RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
2057 RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
2058 RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
2059 RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
2060 RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
2061 RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
2063 #undef RAPIDJSON_STRING_
2065 #if RAPIDJSON_SCHEMA_VERBOSE
2066 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
2067 RAPIDJSON_MULTILINEMACRO_BEGIN\
2068 *documentStack_.template Push<Ch>() = '\0';\
2069 documentStack_.template Pop<Ch>(1);\
2070 internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
2071 RAPIDJSON_MULTILINEMACRO_END
2073 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
2076 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
2077 if (!valid_) return false; \
2078 if (!BeginValue() || !CurrentSchema().method arg1) {\
2079 RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
2080 return valid_ = false;\
2083 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
2084 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
2085 if (context->hasher)\
2086 static_cast<HasherType*>(context->hasher)->method arg2;\
2087 if (context->validators)\
2088 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
2089 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
2090 if (context->patternPropertiesValidators)\
2091 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
2092 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
2095 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
2096 return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
2098 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
2099 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
2100 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
2101 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
2103 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); }
2104 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
2105 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
2106 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
2107 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
2108 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
2109 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
2110 bool RawNumber(const Ch* str, SizeType length, bool copy)
2111 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2112 bool String(const Ch* str, SizeType length, bool copy)
2113 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2115 bool StartObject() {
2116 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
2117 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
2118 return valid_ = !outputHandler_ || outputHandler_->StartObject();
2121 bool Key(const Ch* str, SizeType len, bool copy) {
2122 if (!valid_) return false;
2123 AppendToken(str, len);
2124 if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
2125 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
2126 return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
2129 bool EndObject(SizeType memberCount) {
2130 if (!valid_) return false;
2131 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
2132 if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
2133 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
2137 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
2138 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
2139 return valid_ = !outputHandler_ || outputHandler_->StartArray();
2142 bool EndArray(SizeType elementCount) {
2143 if (!valid_) return false;
2144 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
2145 if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
2146 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
2149 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
2150 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
2151 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
2152 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
2154 // Implementation of ISchemaStateFactory<SchemaType>
2155 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
2156 return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
2157 #if RAPIDJSON_SCHEMA_VERBOSE
2160 &GetStateAllocator());
2163 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
2164 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
2165 v->~GenericSchemaValidator();
2166 StateAllocator::Free(v);
2169 virtual void* CreateHasher() {
2170 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
2173 virtual uint64_t GetHashCode(void* hasher) {
2174 return static_cast<HasherType*>(hasher)->GetHashCode();
2177 virtual void DestroryHasher(void* hasher) {
2178 HasherType* h = static_cast<HasherType*>(hasher);
2180 StateAllocator::Free(h);
2183 virtual void* MallocState(size_t size) {
2184 return GetStateAllocator().Malloc(size);
2187 virtual void FreeState(void* p) {
2188 StateAllocator::Free(p);
2192 typedef typename SchemaType::Context Context;
2193 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
2194 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
2196 GenericSchemaValidator(
2197 const SchemaDocumentType& schemaDocument,
2198 const SchemaType& root,
2199 const char* basePath, size_t basePathSize,
2200 #if RAPIDJSON_SCHEMA_VERBOSE
2203 StateAllocator* allocator = 0,
2204 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2205 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2207 schemaDocument_(&schemaDocument),
2209 stateAllocator_(allocator),
2210 ownStateAllocator_(0),
2211 schemaStack_(allocator, schemaStackCapacity),
2212 documentStack_(allocator, documentStackCapacity),
2214 error_(kObjectType),
2216 missingDependents_(),
2218 #if RAPIDJSON_SCHEMA_VERBOSE
2222 if (basePath && basePathSize)
2223 memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
2226 StateAllocator& GetStateAllocator() {
2227 if (!stateAllocator_)
2228 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
2229 return *stateAllocator_;
2233 if (schemaStack_.Empty())
2236 if (CurrentContext().inArray)
2237 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
2239 if (!CurrentSchema().BeginValue(CurrentContext()))
2242 SizeType count = CurrentContext().patternPropertiesSchemaCount;
2243 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
2244 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
2245 bool valueUniqueness = CurrentContext().valueUniqueness;
2246 RAPIDJSON_ASSERT(CurrentContext().valueSchema);
2247 PushSchema(*CurrentContext().valueSchema);
2250 CurrentContext().objectPatternValidatorType = patternValidatorType;
2251 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
2252 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
2253 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
2254 for (SizeType i = 0; i < count; i++)
2255 va[validatorCount++] = CreateSchemaValidator(*sa[i]);
2258 CurrentContext().arrayUniqueness = valueUniqueness;
2264 if (!CurrentSchema().EndValue(CurrentContext()))
2267 #if RAPIDJSON_SCHEMA_VERBOSE
2268 GenericStringBuffer<EncodingType> sb;
2269 schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
2271 *documentStack_.template Push<Ch>() = '\0';
2272 documentStack_.template Pop<Ch>(1);
2273 internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
2276 uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
2280 if (!schemaStack_.Empty()) {
2281 Context& context = CurrentContext();
2282 if (context.valueUniqueness) {
2283 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
2285 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
2286 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
2287 if (itr->GetUint64() == h) {
2288 DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
2289 RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
2291 a->PushBack(h, GetStateAllocator());
2295 // Remove the last token of document pointer
2296 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
2302 void AppendToken(const Ch* str, SizeType len) {
2303 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
2304 *documentStack_.template PushUnsafe<Ch>() = '/';
2305 for (SizeType i = 0; i < len; i++) {
2306 if (str[i] == '~') {
2307 *documentStack_.template PushUnsafe<Ch>() = '~';
2308 *documentStack_.template PushUnsafe<Ch>() = '0';
2310 else if (str[i] == '/') {
2311 *documentStack_.template PushUnsafe<Ch>() = '~';
2312 *documentStack_.template PushUnsafe<Ch>() = '1';
2315 *documentStack_.template PushUnsafe<Ch>() = str[i];
2319 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema); }
2321 RAPIDJSON_FORCEINLINE void PopSchema() {
2322 Context* c = schemaStack_.template Pop<Context>(1);
2323 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
2324 a->~HashCodeArray();
2325 StateAllocator::Free(a);
2330 void AddErrorLocation(ValueType& result, bool parent) {
2331 GenericStringBuffer<EncodingType> sb;
2332 PointerType instancePointer = GetInvalidDocumentPointer();
2333 ((parent && instancePointer.GetTokenCount() > 0)
2334 ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
2335 : instancePointer).StringifyUriFragment(sb);
2336 ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
2337 GetStateAllocator());
2338 result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
2340 memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()),
2341 CurrentSchema().GetURI().GetString(),
2342 CurrentSchema().GetURI().GetStringLength() * sizeof(Ch));
2343 GetInvalidSchemaPointer().StringifyUriFragment(sb);
2344 ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
2345 GetStateAllocator());
2346 result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
2349 void AddError(ValueType& keyword, ValueType& error) {
2350 typename ValueType::MemberIterator member = error_.FindMember(keyword);
2351 if (member == error_.MemberEnd())
2352 error_.AddMember(keyword, error, GetStateAllocator());
2354 if (member->value.IsObject()) {
2355 ValueType errors(kArrayType);
2356 errors.PushBack(member->value, GetStateAllocator());
2357 member->value = errors;
2359 member->value.PushBack(error, GetStateAllocator());
2363 void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) {
2364 AddErrorLocation(currentError_, parent);
2365 AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_);
2368 void MergeError(ValueType& other) {
2369 for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
2370 AddError(it->name, it->value);
2374 void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected,
2375 const typename SchemaType::ValueType& (*exclusive)() = 0) {
2376 currentError_.SetObject();
2377 currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
2378 currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
2380 currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
2381 AddCurrentError(keyword);
2384 void AddErrorArray(const typename SchemaType::ValueType& keyword,
2385 ISchemaValidator** subvalidators, SizeType count) {
2386 ValueType errors(kArrayType);
2387 for (SizeType i = 0; i < count; ++i)
2388 errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
2389 currentError_.SetObject();
2390 currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
2391 AddCurrentError(keyword);
2394 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
2395 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
2396 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
2398 static const size_t kDefaultSchemaStackCapacity = 1024;
2399 static const size_t kDefaultDocumentStackCapacity = 256;
2400 const SchemaDocumentType* schemaDocument_;
2401 const SchemaType& root_;
2402 StateAllocator* stateAllocator_;
2403 StateAllocator* ownStateAllocator_;
2404 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
2405 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
2406 OutputHandler* outputHandler_;
2408 ValueType currentError_;
2409 ValueType missingDependents_;
2411 #if RAPIDJSON_SCHEMA_VERBOSE
2416 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
2418 ///////////////////////////////////////////////////////////////////////////////
2419 // SchemaValidatingReader
2421 //! A helper class for parsing with validation.
2423 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
2425 \tparam parseFlags Combination of \ref ParseFlag.
2426 \tparam InputStream Type of input stream, implementing Stream concept.
2427 \tparam SourceEncoding Encoding of the input stream.
2428 \tparam SchemaDocumentType Type of schema document.
2429 \tparam StackAllocator Allocator type for stack.
2432 unsigned parseFlags,
2433 typename InputStream,
2434 typename SourceEncoding,
2435 typename SchemaDocumentType = SchemaDocument,
2436 typename StackAllocator = CrtAllocator>
2437 class SchemaValidatingReader {
2439 typedef typename SchemaDocumentType::PointerType PointerType;
2440 typedef typename InputStream::Ch Ch;
2441 typedef GenericValue<SourceEncoding, StackAllocator> ValueType;
2445 \param is Input stream.
2446 \param sd Schema document.
2448 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {}
2450 template <typename Handler>
2451 bool operator()(Handler& handler) {
2452 GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
2453 GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
2454 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
2456 isValid_ = validator.IsValid();
2458 invalidSchemaPointer_ = PointerType();
2459 invalidSchemaKeyword_ = 0;
2460 invalidDocumentPointer_ = PointerType();
2464 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
2465 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
2466 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
2467 error_.CopyFrom(validator.GetError(), allocator_);
2470 return parseResult_;
2473 const ParseResult& GetParseResult() const { return parseResult_; }
2474 bool IsValid() const { return isValid_; }
2475 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
2476 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
2477 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
2478 const ValueType& GetError() const { return error_; }
2482 const SchemaDocumentType& sd_;
2484 ParseResult parseResult_;
2485 PointerType invalidSchemaPointer_;
2486 const Ch* invalidSchemaKeyword_;
2487 PointerType invalidDocumentPointer_;
2488 StackAllocator allocator_;
2493 RAPIDJSON_NAMESPACE_END
2496 #endif // RAPIDJSON_SCHEMA_H_