1 // Tencent is pleased to support the open source community by making RapidJSON available->
2 //
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
4 //
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
7 //
8 // http://opensource->org/licenses/MIT
9 //
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->
14
15 #ifndef RAPIDJSON_SCHEMA_H_
16 #define RAPIDJSON_SCHEMA_H_
17
18 #include "document.h"
19 #include "pointer.h"
20 #include "stringbuffer.h"
21 #include "error/en.h"
22 #include <cmath> // abs, floor
23
24 #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
25 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
26 #else
27 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
28 #endif
29
30 #if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
31 #define RAPIDJSON_SCHEMA_USE_STDREGEX 1
32 #else
33 #define RAPIDJSON_SCHEMA_USE_STDREGEX 0
34 #endif
35
36 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
37 #include "internal/regex.h"
38 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
39 #include <regex>
40 #endif
41
42 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
43 #define RAPIDJSON_SCHEMA_HAS_REGEX 1
44 #else
45 #define RAPIDJSON_SCHEMA_HAS_REGEX 0
46 #endif
47
48 #ifndef RAPIDJSON_SCHEMA_VERBOSE
49 #define RAPIDJSON_SCHEMA_VERBOSE 0
50 #endif
51
52 #if RAPIDJSON_SCHEMA_VERBOSE
53 #include "stringbuffer.h"
54 #endif
55
56 RAPIDJSON_DIAG_PUSH
57
58 #if defined(__GNUC__)
59 RAPIDJSON_DIAG_OFF(effc++)
60 #endif
61
62 #ifdef __clang__
63 RAPIDJSON_DIAG_OFF(weak-vtables)
64 RAPIDJSON_DIAG_OFF(exit-time-destructors)
65 RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
66 RAPIDJSON_DIAG_OFF(variadic-macros)
67 #elif defined(_MSC_VER)
68 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
69 #endif
70
71 RAPIDJSON_NAMESPACE_BEGIN
72
73 ///////////////////////////////////////////////////////////////////////////////
74 // Verbose Utilities
75
76 #if RAPIDJSON_SCHEMA_VERBOSE
77
78 namespace internal {
79
PrintInvalidKeyword(const char * keyword)80 inline void PrintInvalidKeyword(const char* keyword) {
81 printf("Fail keyword: %s\n", keyword);
82 }
83
PrintInvalidKeyword(const wchar_t * keyword)84 inline void PrintInvalidKeyword(const wchar_t* keyword) {
85 wprintf(L"Fail keyword: %ls\n", keyword);
86 }
87
PrintInvalidDocument(const char * document)88 inline void PrintInvalidDocument(const char* document) {
89 printf("Fail document: %s\n\n", document);
90 }
91
PrintInvalidDocument(const wchar_t * document)92 inline void PrintInvalidDocument(const wchar_t* document) {
93 wprintf(L"Fail document: %ls\n\n", document);
94 }
95
PrintValidatorPointers(unsigned depth,const char * s,const char * d)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);
98 }
99
PrintValidatorPointers(unsigned depth,const wchar_t * s,const wchar_t * 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);
102 }
103
104 } // namespace internal
105
106 #endif // RAPIDJSON_SCHEMA_VERBOSE
107
108 ///////////////////////////////////////////////////////////////////////////////
109 // RAPIDJSON_INVALID_KEYWORD_RETURN
110
111 #if RAPIDJSON_SCHEMA_VERBOSE
112 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
113 #else
114 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
115 #endif
116
117 #define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
118 RAPIDJSON_MULTILINEMACRO_BEGIN\
119 context.invalidCode = code;\
120 context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
121 RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\
122 return false;\
123 RAPIDJSON_MULTILINEMACRO_END
124
125 ///////////////////////////////////////////////////////////////////////////////
126 // ValidateFlag
127
128 /*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
129 \ingroup RAPIDJSON_CONFIG
130 \brief User-defined kValidateDefaultFlags definition.
131
132 User can define this as any \c ValidateFlag combinations.
133 */
134 #ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
135 #define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
136 #endif
137
138 //! Combination of validate flags
139 /*! \see
140 */
141 enum ValidateFlag {
142 kValidateNoFlags = 0, //!< No flags are set.
143 kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
144 kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
145 };
146
147 ///////////////////////////////////////////////////////////////////////////////
148 // Forward declarations
149
150 template <typename ValueType, typename Allocator>
151 class GenericSchemaDocument;
152
153 namespace internal {
154
155 template <typename SchemaDocumentType>
156 class Schema;
157
158 ///////////////////////////////////////////////////////////////////////////////
159 // ISchemaValidator
160
161 class ISchemaValidator {
162 public:
~ISchemaValidator()163 virtual ~ISchemaValidator() {}
164 virtual bool IsValid() const = 0;
165 virtual void SetValidateFlags(unsigned flags) = 0;
166 virtual unsigned GetValidateFlags() const = 0;
167 };
168
169 ///////////////////////////////////////////////////////////////////////////////
170 // ISchemaStateFactory
171
172 template <typename SchemaType>
173 class ISchemaStateFactory {
174 public:
~ISchemaStateFactory()175 virtual ~ISchemaStateFactory() {}
176 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
177 virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
178 virtual void* CreateHasher() = 0;
179 virtual uint64_t GetHashCode(void* hasher) = 0;
180 virtual void DestroryHasher(void* hasher) = 0;
181 virtual void* MallocState(size_t size) = 0;
182 virtual void FreeState(void* p) = 0;
183 };
184
185 ///////////////////////////////////////////////////////////////////////////////
186 // IValidationErrorHandler
187
188 template <typename SchemaType>
189 class IValidationErrorHandler {
190 public:
191 typedef typename SchemaType::Ch Ch;
192 typedef typename SchemaType::SValue SValue;
193
~IValidationErrorHandler()194 virtual ~IValidationErrorHandler() {}
195
196 virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
197 virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
198 virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
199 virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
200 virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
201 virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
202 virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
203 virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
204 virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
205
206 virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
207 virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
208 virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
209
210 virtual void DisallowedItem(SizeType index) = 0;
211 virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
212 virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
213 virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
214
215 virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
216 virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
217 virtual void StartMissingProperties() = 0;
218 virtual void AddMissingProperty(const SValue& name) = 0;
219 virtual bool EndMissingProperties() = 0;
220 virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
221 virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
222
223 virtual void StartDependencyErrors() = 0;
224 virtual void StartMissingDependentProperties() = 0;
225 virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
226 virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
227 virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
228 virtual bool EndDependencyErrors() = 0;
229
230 virtual void DisallowedValue(const ValidateErrorCode code) = 0;
231 virtual void StartDisallowedType() = 0;
232 virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
233 virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
234 virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
235 virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
236 virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0;
237 virtual void Disallowed() = 0;
238 };
239
240
241 ///////////////////////////////////////////////////////////////////////////////
242 // Hasher
243
244 // For comparison of compound value
245 template<typename Encoding, typename Allocator>
246 class Hasher {
247 public:
248 typedef typename Encoding::Ch Ch;
249
stack_(allocator,stackCapacity)250 Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
251
Null()252 bool Null() { return WriteType(kNullType); }
Bool(bool b)253 bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
Int(int i)254 bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
Uint(unsigned u)255 bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
Int64(int64_t i)256 bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
Uint64(uint64_t u)257 bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
Double(double d)258 bool Double(double d) {
259 Number n;
260 if (d < 0) n.u.i = static_cast<int64_t>(d);
261 else n.u.u = static_cast<uint64_t>(d);
262 n.d = d;
263 return WriteNumber(n);
264 }
265
RawNumber(const Ch * str,SizeType len,bool)266 bool RawNumber(const Ch* str, SizeType len, bool) {
267 WriteBuffer(kNumberType, str, len * sizeof(Ch));
268 return true;
269 }
270
String(const Ch * str,SizeType len,bool)271 bool String(const Ch* str, SizeType len, bool) {
272 WriteBuffer(kStringType, str, len * sizeof(Ch));
273 return true;
274 }
275
StartObject()276 bool StartObject() { return true; }
Key(const Ch * str,SizeType len,bool copy)277 bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
EndObject(SizeType memberCount)278 bool EndObject(SizeType memberCount) {
279 uint64_t h = Hash(0, kObjectType);
280 uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
281 for (SizeType i = 0; i < memberCount; i++)
282 h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
283 *stack_.template Push<uint64_t>() = h;
284 return true;
285 }
286
StartArray()287 bool StartArray() { return true; }
EndArray(SizeType elementCount)288 bool EndArray(SizeType elementCount) {
289 uint64_t h = Hash(0, kArrayType);
290 uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
291 for (SizeType i = 0; i < elementCount; i++)
292 h = Hash(h, e[i]); // Use hash to achieve element order sensitive
293 *stack_.template Push<uint64_t>() = h;
294 return true;
295 }
296
IsValid()297 bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
298
GetHashCode()299 uint64_t GetHashCode() const {
300 RAPIDJSON_ASSERT(IsValid());
301 return *stack_.template Top<uint64_t>();
302 }
303
304 private:
305 static const size_t kDefaultSize = 256;
306 struct Number {
307 union U {
308 uint64_t u;
309 int64_t i;
310 }u;
311 double d;
312 };
313
WriteType(Type type)314 bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
315
WriteNumber(const Number & n)316 bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
317
WriteBuffer(Type type,const void * data,size_t len)318 bool WriteBuffer(Type type, const void* data, size_t len) {
319 // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
320 uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
321 const unsigned char* d = static_cast<const unsigned char*>(data);
322 for (size_t i = 0; i < len; i++)
323 h = Hash(h, d[i]);
324 *stack_.template Push<uint64_t>() = h;
325 return true;
326 }
327
Hash(uint64_t h,uint64_t d)328 static uint64_t Hash(uint64_t h, uint64_t d) {
329 static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
330 h ^= d;
331 h *= kPrime;
332 return h;
333 }
334
335 Stack<Allocator> stack_;
336 };
337
338 ///////////////////////////////////////////////////////////////////////////////
339 // SchemaValidationContext
340
341 template <typename SchemaDocumentType>
342 struct SchemaValidationContext {
343 typedef Schema<SchemaDocumentType> SchemaType;
344 typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
345 typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
346 typedef typename SchemaType::ValueType ValueType;
347 typedef typename ValueType::Ch Ch;
348
349 enum PatternValidatorType {
350 kPatternValidatorOnly,
351 kPatternValidatorWithProperty,
352 kPatternValidatorWithAdditionalProperty
353 };
354
SchemaValidationContextSchemaValidationContext355 SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) :
356 factory(f),
357 error_handler(eh),
358 schema(s),
359 valueSchema(),
360 invalidKeyword(),
361 invalidCode(),
362 hasher(),
363 arrayElementHashCodes(),
364 validators(),
365 validatorCount(),
366 patternPropertiesValidators(),
367 patternPropertiesValidatorCount(),
368 patternPropertiesSchemas(),
369 patternPropertiesSchemaCount(),
370 valuePatternValidatorType(kPatternValidatorOnly),
371 propertyExist(),
372 inArray(false),
373 valueUniqueness(false),
374 arrayUniqueness(false)
375 {
376 }
377
~SchemaValidationContextSchemaValidationContext378 ~SchemaValidationContext() {
379 if (hasher)
380 factory.DestroryHasher(hasher);
381 if (validators) {
382 for (SizeType i = 0; i < validatorCount; i++)
383 factory.DestroySchemaValidator(validators[i]);
384 factory.FreeState(validators);
385 }
386 if (patternPropertiesValidators) {
387 for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
388 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
389 factory.FreeState(patternPropertiesValidators);
390 }
391 if (patternPropertiesSchemas)
392 factory.FreeState(patternPropertiesSchemas);
393 if (propertyExist)
394 factory.FreeState(propertyExist);
395 }
396
397 SchemaValidatorFactoryType& factory;
398 ErrorHandlerType& error_handler;
399 const SchemaType* schema;
400 const SchemaType* valueSchema;
401 const Ch* invalidKeyword;
402 ValidateErrorCode invalidCode;
403 void* hasher; // Only validator access
404 void* arrayElementHashCodes; // Only validator access this
405 ISchemaValidator** validators;
406 SizeType validatorCount;
407 ISchemaValidator** patternPropertiesValidators;
408 SizeType patternPropertiesValidatorCount;
409 const SchemaType** patternPropertiesSchemas;
410 SizeType patternPropertiesSchemaCount;
411 PatternValidatorType valuePatternValidatorType;
412 PatternValidatorType objectPatternValidatorType;
413 SizeType arrayElementIndex;
414 bool* propertyExist;
415 bool inArray;
416 bool valueUniqueness;
417 bool arrayUniqueness;
418 };
419
420 ///////////////////////////////////////////////////////////////////////////////
421 // Schema
422
423 template <typename SchemaDocumentType>
424 class Schema {
425 public:
426 typedef typename SchemaDocumentType::ValueType ValueType;
427 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
428 typedef typename SchemaDocumentType::PointerType PointerType;
429 typedef typename ValueType::EncodingType EncodingType;
430 typedef typename EncodingType::Ch Ch;
431 typedef SchemaValidationContext<SchemaDocumentType> Context;
432 typedef Schema<SchemaDocumentType> SchemaType;
433 typedef GenericValue<EncodingType, AllocatorType> SValue;
434 typedef IValidationErrorHandler<Schema> ErrorHandler;
435 friend class GenericSchemaDocument<ValueType, AllocatorType>;
436
Schema(SchemaDocumentType * schemaDocument,const PointerType & p,const ValueType & value,const ValueType & document,AllocatorType * allocator)437 Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
438 allocator_(allocator),
439 uri_(schemaDocument->GetURI(), *allocator),
440 pointer_(p, allocator),
441 typeless_(schemaDocument->GetTypeless()),
442 enum_(),
443 enumCount_(),
444 not_(),
445 type_((1 << kTotalSchemaType) - 1), // typeless
446 validatorCount_(),
447 notValidatorIndex_(),
448 properties_(),
449 additionalPropertiesSchema_(),
450 patternProperties_(),
451 patternPropertyCount_(),
452 propertyCount_(),
453 minProperties_(),
454 maxProperties_(SizeType(~0)),
455 additionalProperties_(true),
456 hasDependencies_(),
457 hasRequired_(),
458 hasSchemaDependencies_(),
459 additionalItemsSchema_(),
460 itemsList_(),
461 itemsTuple_(),
462 itemsTupleCount_(),
463 minItems_(),
464 maxItems_(SizeType(~0)),
465 additionalItems_(true),
466 uniqueItems_(false),
467 pattern_(),
468 minLength_(0),
469 maxLength_(~SizeType(0)),
470 exclusiveMinimum_(false),
471 exclusiveMaximum_(false),
472 defaultValueLength_(0)
473 {
474 typedef typename ValueType::ConstValueIterator ConstValueIterator;
475 typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
476
477 if (!value.IsObject())
478 return;
479
480 if (const ValueType* v = GetMember(value, GetTypeString())) {
481 type_ = 0;
482 if (v->IsString())
483 AddType(*v);
484 else if (v->IsArray())
485 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
486 AddType(*itr);
487 }
488
489 if (const ValueType* v = GetMember(value, GetEnumString())) {
490 if (v->IsArray() && v->Size() > 0) {
491 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
492 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
493 typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
494 char buffer[256u + 24];
495 MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
496 EnumHasherType h(&hasherAllocator, 256);
497 itr->Accept(h);
498 enum_[enumCount_++] = h.GetHashCode();
499 }
500 }
501 }
502
503 if (schemaDocument) {
504 AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
505 AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
506 AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
507 }
508
509 if (const ValueType* v = GetMember(value, GetNotString())) {
510 schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document);
511 notValidatorIndex_ = validatorCount_;
512 validatorCount_++;
513 }
514
515 // Object
516
517 const ValueType* properties = GetMember(value, GetPropertiesString());
518 const ValueType* required = GetMember(value, GetRequiredString());
519 const ValueType* dependencies = GetMember(value, GetDependenciesString());
520 {
521 // Gather properties from properties/required/dependencies
522 SValue allProperties(kArrayType);
523
524 if (properties && properties->IsObject())
525 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
526 AddUniqueElement(allProperties, itr->name);
527
528 if (required && required->IsArray())
529 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
530 if (itr->IsString())
531 AddUniqueElement(allProperties, *itr);
532
533 if (dependencies && dependencies->IsObject())
534 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
535 AddUniqueElement(allProperties, itr->name);
536 if (itr->value.IsArray())
537 for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
538 if (i->IsString())
539 AddUniqueElement(allProperties, *i);
540 }
541
542 if (allProperties.Size() > 0) {
543 propertyCount_ = allProperties.Size();
544 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
545 for (SizeType i = 0; i < propertyCount_; i++) {
546 new (&properties_[i]) Property();
547 properties_[i].name = allProperties[i];
548 properties_[i].schema = typeless_;
549 }
550 }
551 }
552
553 if (properties && properties->IsObject()) {
554 PointerType q = p.Append(GetPropertiesString(), allocator_);
555 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
556 SizeType index;
557 if (FindPropertyIndex(itr->name, &index))
558 schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
559 }
560 }
561
562 if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
563 PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
564 patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
565 patternPropertyCount_ = 0;
566
567 for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
568 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
569 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
570 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
571 patternPropertyCount_++;
572 }
573 }
574
575 if (required && required->IsArray())
576 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
577 if (itr->IsString()) {
578 SizeType index;
579 if (FindPropertyIndex(*itr, &index)) {
580 properties_[index].required = true;
581 hasRequired_ = true;
582 }
583 }
584
585 if (dependencies && dependencies->IsObject()) {
586 PointerType q = p.Append(GetDependenciesString(), allocator_);
587 hasDependencies_ = true;
588 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
589 SizeType sourceIndex;
590 if (FindPropertyIndex(itr->name, &sourceIndex)) {
591 if (itr->value.IsArray()) {
592 properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
593 std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
594 for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
595 SizeType targetIndex;
596 if (FindPropertyIndex(*targetItr, &targetIndex))
597 properties_[sourceIndex].dependencies[targetIndex] = true;
598 }
599 }
600 else if (itr->value.IsObject()) {
601 hasSchemaDependencies_ = true;
602 schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
603 properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
604 validatorCount_++;
605 }
606 }
607 }
608 }
609
610 if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
611 if (v->IsBool())
612 additionalProperties_ = v->GetBool();
613 else if (v->IsObject())
614 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
615 }
616
617 AssignIfExist(minProperties_, value, GetMinPropertiesString());
618 AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
619
620 // Array
621 if (const ValueType* v = GetMember(value, GetItemsString())) {
622 PointerType q = p.Append(GetItemsString(), allocator_);
623 if (v->IsObject()) // List validation
624 schemaDocument->CreateSchema(&itemsList_, q, *v, document);
625 else if (v->IsArray()) { // Tuple validation
626 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
627 SizeType index = 0;
628 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
629 schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
630 }
631 }
632
633 AssignIfExist(minItems_, value, GetMinItemsString());
634 AssignIfExist(maxItems_, value, GetMaxItemsString());
635
636 if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
637 if (v->IsBool())
638 additionalItems_ = v->GetBool();
639 else if (v->IsObject())
640 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
641 }
642
643 AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
644
645 // String
646 AssignIfExist(minLength_, value, GetMinLengthString());
647 AssignIfExist(maxLength_, value, GetMaxLengthString());
648
649 if (const ValueType* v = GetMember(value, GetPatternString()))
650 pattern_ = CreatePattern(*v);
651
652 // Number
653 if (const ValueType* v = GetMember(value, GetMinimumString()))
654 if (v->IsNumber())
655 minimum_.CopyFrom(*v, *allocator_);
656
657 if (const ValueType* v = GetMember(value, GetMaximumString()))
658 if (v->IsNumber())
659 maximum_.CopyFrom(*v, *allocator_);
660
661 AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
662 AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
663
664 if (const ValueType* v = GetMember(value, GetMultipleOfString()))
665 if (v->IsNumber() && v->GetDouble() > 0.0)
666 multipleOf_.CopyFrom(*v, *allocator_);
667
668 // Default
669 if (const ValueType* v = GetMember(value, GetDefaultValueString()))
670 if (v->IsString())
671 defaultValueLength_ = v->GetStringLength();
672
673 }
674
~Schema()675 ~Schema() {
676 AllocatorType::Free(enum_);
677 if (properties_) {
678 for (SizeType i = 0; i < propertyCount_; i++)
679 properties_[i].~Property();
680 AllocatorType::Free(properties_);
681 }
682 if (patternProperties_) {
683 for (SizeType i = 0; i < patternPropertyCount_; i++)
684 patternProperties_[i].~PatternProperty();
685 AllocatorType::Free(patternProperties_);
686 }
687 AllocatorType::Free(itemsTuple_);
688 #if RAPIDJSON_SCHEMA_HAS_REGEX
689 if (pattern_) {
690 pattern_->~RegexType();
691 AllocatorType::Free(pattern_);
692 }
693 #endif
694 }
695
GetURI()696 const SValue& GetURI() const {
697 return uri_;
698 }
699
GetPointer()700 const PointerType& GetPointer() const {
701 return pointer_;
702 }
703
BeginValue(Context & context)704 bool BeginValue(Context& context) const {
705 if (context.inArray) {
706 if (uniqueItems_)
707 context.valueUniqueness = true;
708
709 if (itemsList_)
710 context.valueSchema = itemsList_;
711 else if (itemsTuple_) {
712 if (context.arrayElementIndex < itemsTupleCount_)
713 context.valueSchema = itemsTuple_[context.arrayElementIndex];
714 else if (additionalItemsSchema_)
715 context.valueSchema = additionalItemsSchema_;
716 else if (additionalItems_)
717 context.valueSchema = typeless_;
718 else {
719 context.error_handler.DisallowedItem(context.arrayElementIndex);
720 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
721 context.valueSchema = typeless_;
722 // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
723 context.arrayElementIndex++;
724 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
725 }
726 }
727 else
728 context.valueSchema = typeless_;
729
730 context.arrayElementIndex++;
731 }
732 return true;
733 }
734
EndValue(Context & context)735 RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
736 if (context.patternPropertiesValidatorCount > 0) {
737 bool otherValid = false;
738 SizeType count = context.patternPropertiesValidatorCount;
739 if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
740 otherValid = context.patternPropertiesValidators[--count]->IsValid();
741
742 bool patternValid = true;
743 for (SizeType i = 0; i < count; i++)
744 if (!context.patternPropertiesValidators[i]->IsValid()) {
745 patternValid = false;
746 break;
747 }
748
749 if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
750 if (!patternValid) {
751 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
752 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
753 }
754 }
755 else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
756 if (!patternValid || !otherValid) {
757 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
758 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
759 }
760 }
761 else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
762 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
763 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
764 }
765 }
766
767 // For enums only check if we have a hasher
768 if (enum_ && context.hasher) {
769 const uint64_t h = context.factory.GetHashCode(context.hasher);
770 for (SizeType i = 0; i < enumCount_; i++)
771 if (enum_[i] == h)
772 goto foundEnum;
773 context.error_handler.DisallowedValue(kValidateErrorEnum);
774 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
775 foundEnum:;
776 }
777
778 if (allOf_.schemas)
779 for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
780 if (!context.validators[i]->IsValid()) {
781 context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
782 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
783 }
784
785 if (anyOf_.schemas) {
786 for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
787 if (context.validators[i]->IsValid())
788 goto foundAny;
789 context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
790 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
791 foundAny:;
792 }
793
794 if (oneOf_.schemas) {
795 bool oneValid = false;
796 for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
797 if (context.validators[i]->IsValid()) {
798 if (oneValid) {
799 context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true);
800 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
801 } else
802 oneValid = true;
803 }
804 if (!oneValid) {
805 context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false);
806 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
807 }
808 }
809
810 if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
811 context.error_handler.Disallowed();
812 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
813 }
814
815 return true;
816 }
817
Null(Context & context)818 bool Null(Context& context) const {
819 if (!(type_ & (1 << kNullSchemaType))) {
820 DisallowedType(context, GetNullString());
821 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
822 }
823 return CreateParallelValidator(context);
824 }
825
Bool(Context & context,bool)826 bool Bool(Context& context, bool) const {
827 if (!(type_ & (1 << kBooleanSchemaType))) {
828 DisallowedType(context, GetBooleanString());
829 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
830 }
831 return CreateParallelValidator(context);
832 }
833
Int(Context & context,int i)834 bool Int(Context& context, int i) const {
835 if (!CheckInt(context, i))
836 return false;
837 return CreateParallelValidator(context);
838 }
839
Uint(Context & context,unsigned u)840 bool Uint(Context& context, unsigned u) const {
841 if (!CheckUint(context, u))
842 return false;
843 return CreateParallelValidator(context);
844 }
845
Int64(Context & context,int64_t i)846 bool Int64(Context& context, int64_t i) const {
847 if (!CheckInt(context, i))
848 return false;
849 return CreateParallelValidator(context);
850 }
851
Uint64(Context & context,uint64_t u)852 bool Uint64(Context& context, uint64_t u) const {
853 if (!CheckUint(context, u))
854 return false;
855 return CreateParallelValidator(context);
856 }
857
Double(Context & context,double d)858 bool Double(Context& context, double d) const {
859 if (!(type_ & (1 << kNumberSchemaType))) {
860 DisallowedType(context, GetNumberString());
861 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
862 }
863
864 if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
865 return false;
866
867 if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
868 return false;
869
870 if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
871 return false;
872
873 return CreateParallelValidator(context);
874 }
875
String(Context & context,const Ch * str,SizeType length,bool)876 bool String(Context& context, const Ch* str, SizeType length, bool) const {
877 if (!(type_ & (1 << kStringSchemaType))) {
878 DisallowedType(context, GetStringString());
879 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
880 }
881
882 if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
883 SizeType count;
884 if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
885 if (count < minLength_) {
886 context.error_handler.TooShort(str, length, minLength_);
887 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
888 }
889 if (count > maxLength_) {
890 context.error_handler.TooLong(str, length, maxLength_);
891 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
892 }
893 }
894 }
895
896 if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
897 context.error_handler.DoesNotMatch(str, length);
898 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
899 }
900
901 return CreateParallelValidator(context);
902 }
903
StartObject(Context & context)904 bool StartObject(Context& context) const {
905 if (!(type_ & (1 << kObjectSchemaType))) {
906 DisallowedType(context, GetObjectString());
907 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
908 }
909
910 if (hasDependencies_ || hasRequired_) {
911 context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
912 std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
913 }
914
915 if (patternProperties_) { // pre-allocate schema array
916 SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
917 context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
918 context.patternPropertiesSchemaCount = 0;
919 std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
920 }
921
922 return CreateParallelValidator(context);
923 }
924
Key(Context & context,const Ch * str,SizeType len,bool)925 bool Key(Context& context, const Ch* str, SizeType len, bool) const {
926 if (patternProperties_) {
927 context.patternPropertiesSchemaCount = 0;
928 for (SizeType i = 0; i < patternPropertyCount_; i++)
929 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
930 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
931 context.valueSchema = typeless_;
932 }
933 }
934
935 SizeType index = 0;
936 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
937 if (context.patternPropertiesSchemaCount > 0) {
938 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
939 context.valueSchema = typeless_;
940 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
941 }
942 else
943 context.valueSchema = properties_[index].schema;
944
945 if (context.propertyExist)
946 context.propertyExist[index] = true;
947
948 return true;
949 }
950
951 if (additionalPropertiesSchema_) {
952 if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
953 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
954 context.valueSchema = typeless_;
955 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
956 }
957 else
958 context.valueSchema = additionalPropertiesSchema_;
959 return true;
960 }
961 else if (additionalProperties_) {
962 context.valueSchema = typeless_;
963 return true;
964 }
965
966 if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
967 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
968 context.valueSchema = typeless_;
969 context.error_handler.DisallowedProperty(str, len);
970 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
971 }
972
973 return true;
974 }
975
EndObject(Context & context,SizeType memberCount)976 bool EndObject(Context& context, SizeType memberCount) const {
977 if (hasRequired_) {
978 context.error_handler.StartMissingProperties();
979 for (SizeType index = 0; index < propertyCount_; index++)
980 if (properties_[index].required && !context.propertyExist[index])
981 if (properties_[index].schema->defaultValueLength_ == 0 )
982 context.error_handler.AddMissingProperty(properties_[index].name);
983 if (context.error_handler.EndMissingProperties())
984 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
985 }
986
987 if (memberCount < minProperties_) {
988 context.error_handler.TooFewProperties(memberCount, minProperties_);
989 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
990 }
991
992 if (memberCount > maxProperties_) {
993 context.error_handler.TooManyProperties(memberCount, maxProperties_);
994 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
995 }
996
997 if (hasDependencies_) {
998 context.error_handler.StartDependencyErrors();
999 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
1000 const Property& source = properties_[sourceIndex];
1001 if (context.propertyExist[sourceIndex]) {
1002 if (source.dependencies) {
1003 context.error_handler.StartMissingDependentProperties();
1004 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
1005 if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
1006 context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
1007 context.error_handler.EndMissingDependentProperties(source.name);
1008 }
1009 else if (source.dependenciesSchema) {
1010 ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
1011 if (!dependenciesValidator->IsValid())
1012 context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
1013 }
1014 }
1015 }
1016 if (context.error_handler.EndDependencyErrors())
1017 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
1018 }
1019
1020 return true;
1021 }
1022
StartArray(Context & context)1023 bool StartArray(Context& context) const {
1024 context.arrayElementIndex = 0;
1025 context.inArray = true; // Ensure we note that we are in an array
1026
1027 if (!(type_ & (1 << kArraySchemaType))) {
1028 DisallowedType(context, GetArrayString());
1029 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1030 }
1031
1032 return CreateParallelValidator(context);
1033 }
1034
EndArray(Context & context,SizeType elementCount)1035 bool EndArray(Context& context, SizeType elementCount) const {
1036 context.inArray = false;
1037
1038 if (elementCount < minItems_) {
1039 context.error_handler.TooFewItems(elementCount, minItems_);
1040 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
1041 }
1042
1043 if (elementCount > maxItems_) {
1044 context.error_handler.TooManyItems(elementCount, maxItems_);
1045 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
1046 }
1047
1048 return true;
1049 }
1050
GetValidateErrorKeyword(ValidateErrorCode validateErrorCode)1051 static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
1052 switch (validateErrorCode) {
1053 case kValidateErrorMultipleOf: return GetMultipleOfString();
1054 case kValidateErrorMaximum: return GetMaximumString();
1055 case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
1056 case kValidateErrorMinimum: return GetMinimumString();
1057 case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
1058
1059 case kValidateErrorMaxLength: return GetMaxLengthString();
1060 case kValidateErrorMinLength: return GetMinLengthString();
1061 case kValidateErrorPattern: return GetPatternString();
1062
1063 case kValidateErrorMaxItems: return GetMaxItemsString();
1064 case kValidateErrorMinItems: return GetMinItemsString();
1065 case kValidateErrorUniqueItems: return GetUniqueItemsString();
1066 case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
1067
1068 case kValidateErrorMaxProperties: return GetMaxPropertiesString();
1069 case kValidateErrorMinProperties: return GetMinPropertiesString();
1070 case kValidateErrorRequired: return GetRequiredString();
1071 case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
1072 case kValidateErrorPatternProperties: return GetPatternPropertiesString();
1073 case kValidateErrorDependencies: return GetDependenciesString();
1074
1075 case kValidateErrorEnum: return GetEnumString();
1076 case kValidateErrorType: return GetTypeString();
1077
1078 case kValidateErrorOneOf: return GetOneOfString();
1079 case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
1080 case kValidateErrorAllOf: return GetAllOfString();
1081 case kValidateErrorAnyOf: return GetAnyOfString();
1082 case kValidateErrorNot: return GetNotString();
1083
1084 default: return GetNullString();
1085 }
1086 }
1087
1088
1089 // Generate functions for string literal according to Ch
1090 #define RAPIDJSON_STRING_(name, ...) \
1091 static const ValueType& Get##name##String() {\
1092 static const Ch s[] = { __VA_ARGS__, '\0' };\
1093 static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
1094 return v;\
1095 }
1096
1097 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
1098 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
1099 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
1100 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
1101 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
1102 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
1103 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
1104 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
1105 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
1106 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
1107 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
1108 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
1109 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
1110 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1111 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
1112 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
1113 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1114 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1115 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1116 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1117 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
1118 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
1119 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
1120 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
1121 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
1122 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
1123 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
1124 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
1125 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
1126 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
1127 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
1128 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
1129 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
1130 RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
1131
1132 #undef RAPIDJSON_STRING_
1133
1134 private:
1135 enum SchemaValueType {
1136 kNullSchemaType,
1137 kBooleanSchemaType,
1138 kObjectSchemaType,
1139 kArraySchemaType,
1140 kStringSchemaType,
1141 kNumberSchemaType,
1142 kIntegerSchemaType,
1143 kTotalSchemaType
1144 };
1145
1146 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1147 typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
1148 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1149 typedef std::basic_regex<Ch> RegexType;
1150 #else
1151 typedef char RegexType;
1152 #endif
1153
1154 struct SchemaArray {
SchemaArraySchemaArray1155 SchemaArray() : schemas(), count() {}
~SchemaArraySchemaArray1156 ~SchemaArray() { AllocatorType::Free(schemas); }
1157 const SchemaType** schemas;
1158 SizeType begin; // begin index of context.validators
1159 SizeType count;
1160 };
1161
1162 template <typename V1, typename V2>
AddUniqueElement(V1 & a,const V2 & v)1163 void AddUniqueElement(V1& a, const V2& v) {
1164 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
1165 if (*itr == v)
1166 return;
1167 V1 c(v, *allocator_);
1168 a.PushBack(c, *allocator_);
1169 }
1170
GetMember(const ValueType & value,const ValueType & name)1171 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
1172 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
1173 return itr != value.MemberEnd() ? &(itr->value) : 0;
1174 }
1175
AssignIfExist(bool & out,const ValueType & value,const ValueType & name)1176 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
1177 if (const ValueType* v = GetMember(value, name))
1178 if (v->IsBool())
1179 out = v->GetBool();
1180 }
1181
AssignIfExist(SizeType & out,const ValueType & value,const ValueType & name)1182 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
1183 if (const ValueType* v = GetMember(value, name))
1184 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
1185 out = static_cast<SizeType>(v->GetUint64());
1186 }
1187
AssignIfExist(SchemaArray & out,SchemaDocumentType & schemaDocument,const PointerType & p,const ValueType & value,const ValueType & name,const ValueType & document)1188 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
1189 if (const ValueType* v = GetMember(value, name)) {
1190 if (v->IsArray() && v->Size() > 0) {
1191 PointerType q = p.Append(name, allocator_);
1192 out.count = v->Size();
1193 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
1194 memset(out.schemas, 0, sizeof(Schema*)* out.count);
1195 for (SizeType i = 0; i < out.count; i++)
1196 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
1197 out.begin = validatorCount_;
1198 validatorCount_ += out.count;
1199 }
1200 }
1201 }
1202
1203 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1204 template <typename ValueType>
CreatePattern(const ValueType & value)1205 RegexType* CreatePattern(const ValueType& value) {
1206 if (value.IsString()) {
1207 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
1208 if (!r->IsValid()) {
1209 r->~RegexType();
1210 AllocatorType::Free(r);
1211 r = 0;
1212 }
1213 return r;
1214 }
1215 return 0;
1216 }
1217
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType)1218 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1219 GenericRegexSearch<RegexType> rs(*pattern);
1220 return rs.Search(str);
1221 }
1222 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1223 template <typename ValueType>
CreatePattern(const ValueType & value)1224 RegexType* CreatePattern(const ValueType& value) {
1225 if (value.IsString()) {
1226 RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
1227 try {
1228 return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1229 }
1230 catch (const std::regex_error&) {
1231 AllocatorType::Free(r);
1232 }
1233 }
1234 return 0;
1235 }
1236
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType length)1237 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1238 std::match_results<const Ch*> r;
1239 return std::regex_search(str, str + length, r, *pattern);
1240 }
1241 #else
1242 template <typename ValueType>
CreatePattern(const ValueType &)1243 RegexType* CreatePattern(const ValueType&) { return 0; }
1244
IsPatternMatch(const RegexType *,const Ch *,SizeType)1245 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1246 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1247
AddType(const ValueType & type)1248 void AddType(const ValueType& type) {
1249 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1250 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1251 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1252 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1253 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1254 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1255 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1256 }
1257
CreateParallelValidator(Context & context)1258 bool CreateParallelValidator(Context& context) const {
1259 if (enum_ || context.arrayUniqueness)
1260 context.hasher = context.factory.CreateHasher();
1261
1262 if (validatorCount_) {
1263 RAPIDJSON_ASSERT(context.validators == 0);
1264 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1265 context.validatorCount = validatorCount_;
1266
1267 // Always return after first failure for these sub-validators
1268 if (allOf_.schemas)
1269 CreateSchemaValidators(context, allOf_, false);
1270
1271 if (anyOf_.schemas)
1272 CreateSchemaValidators(context, anyOf_, false);
1273
1274 if (oneOf_.schemas)
1275 CreateSchemaValidators(context, oneOf_, false);
1276
1277 if (not_)
1278 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
1279
1280 if (hasSchemaDependencies_) {
1281 for (SizeType i = 0; i < propertyCount_; i++)
1282 if (properties_[i].dependenciesSchema)
1283 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
1284 }
1285 }
1286
1287 return true;
1288 }
1289
CreateSchemaValidators(Context & context,const SchemaArray & schemas,const bool inheritContinueOnErrors)1290 void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
1291 for (SizeType i = 0; i < schemas.count; i++)
1292 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
1293 }
1294
1295 // O(n)
FindPropertyIndex(const ValueType & name,SizeType * outIndex)1296 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1297 SizeType len = name.GetStringLength();
1298 const Ch* str = name.GetString();
1299 for (SizeType index = 0; index < propertyCount_; index++)
1300 if (properties_[index].name.GetStringLength() == len &&
1301 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1302 {
1303 *outIndex = index;
1304 return true;
1305 }
1306 return false;
1307 }
1308
CheckInt(Context & context,int64_t i)1309 bool CheckInt(Context& context, int64_t i) const {
1310 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1311 DisallowedType(context, GetIntegerString());
1312 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1313 }
1314
1315 if (!minimum_.IsNull()) {
1316 if (minimum_.IsInt64()) {
1317 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
1318 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1319 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1320 }
1321 }
1322 else if (minimum_.IsUint64()) {
1323 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1324 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
1325 }
1326 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1327 return false;
1328 }
1329
1330 if (!maximum_.IsNull()) {
1331 if (maximum_.IsInt64()) {
1332 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
1333 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1334 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1335 }
1336 }
1337 else if (maximum_.IsUint64()) { }
1338 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1339 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1340 return false;
1341 }
1342
1343 if (!multipleOf_.IsNull()) {
1344 if (multipleOf_.IsUint64()) {
1345 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
1346 context.error_handler.NotMultipleOf(i, multipleOf_);
1347 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1348 }
1349 }
1350 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1351 return false;
1352 }
1353
1354 return true;
1355 }
1356
CheckUint(Context & context,uint64_t i)1357 bool CheckUint(Context& context, uint64_t i) const {
1358 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1359 DisallowedType(context, GetIntegerString());
1360 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1361 }
1362
1363 if (!minimum_.IsNull()) {
1364 if (minimum_.IsUint64()) {
1365 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
1366 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1367 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1368 }
1369 }
1370 else if (minimum_.IsInt64())
1371 /* do nothing */; // i >= 0 > minimum.Getint64()
1372 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1373 return false;
1374 }
1375
1376 if (!maximum_.IsNull()) {
1377 if (maximum_.IsUint64()) {
1378 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
1379 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1380 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1381 }
1382 }
1383 else if (maximum_.IsInt64()) {
1384 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1385 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
1386 }
1387 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1388 return false;
1389 }
1390
1391 if (!multipleOf_.IsNull()) {
1392 if (multipleOf_.IsUint64()) {
1393 if (i % multipleOf_.GetUint64() != 0) {
1394 context.error_handler.NotMultipleOf(i, multipleOf_);
1395 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1396 }
1397 }
1398 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1399 return false;
1400 }
1401
1402 return true;
1403 }
1404
CheckDoubleMinimum(Context & context,double d)1405 bool CheckDoubleMinimum(Context& context, double d) const {
1406 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
1407 context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
1408 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1409 }
1410 return true;
1411 }
1412
CheckDoubleMaximum(Context & context,double d)1413 bool CheckDoubleMaximum(Context& context, double d) const {
1414 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
1415 context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
1416 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1417 }
1418 return true;
1419 }
1420
CheckDoubleMultipleOf(Context & context,double d)1421 bool CheckDoubleMultipleOf(Context& context, double d) const {
1422 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1423 double q = std::floor(a / b);
1424 double r = a - q * b;
1425 if (r > 0.0) {
1426 context.error_handler.NotMultipleOf(d, multipleOf_);
1427 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1428 }
1429 return true;
1430 }
1431
DisallowedType(Context & context,const ValueType & actualType)1432 void DisallowedType(Context& context, const ValueType& actualType) const {
1433 ErrorHandler& eh = context.error_handler;
1434 eh.StartDisallowedType();
1435
1436 if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
1437 if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
1438 if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
1439 if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
1440 if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
1441
1442 if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
1443 else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
1444
1445 eh.EndDisallowedType(actualType);
1446 }
1447
1448 struct Property {
PropertyProperty1449 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
~PropertyProperty1450 ~Property() { AllocatorType::Free(dependencies); }
1451 SValue name;
1452 const SchemaType* schema;
1453 const SchemaType* dependenciesSchema;
1454 SizeType dependenciesValidatorIndex;
1455 bool* dependencies;
1456 bool required;
1457 };
1458
1459 struct PatternProperty {
PatternPropertyPatternProperty1460 PatternProperty() : schema(), pattern() {}
~PatternPropertyPatternProperty1461 ~PatternProperty() {
1462 if (pattern) {
1463 pattern->~RegexType();
1464 AllocatorType::Free(pattern);
1465 }
1466 }
1467 const SchemaType* schema;
1468 RegexType* pattern;
1469 };
1470
1471 AllocatorType* allocator_;
1472 SValue uri_;
1473 PointerType pointer_;
1474 const SchemaType* typeless_;
1475 uint64_t* enum_;
1476 SizeType enumCount_;
1477 SchemaArray allOf_;
1478 SchemaArray anyOf_;
1479 SchemaArray oneOf_;
1480 const SchemaType* not_;
1481 unsigned type_; // bitmask of kSchemaType
1482 SizeType validatorCount_;
1483 SizeType notValidatorIndex_;
1484
1485 Property* properties_;
1486 const SchemaType* additionalPropertiesSchema_;
1487 PatternProperty* patternProperties_;
1488 SizeType patternPropertyCount_;
1489 SizeType propertyCount_;
1490 SizeType minProperties_;
1491 SizeType maxProperties_;
1492 bool additionalProperties_;
1493 bool hasDependencies_;
1494 bool hasRequired_;
1495 bool hasSchemaDependencies_;
1496
1497 const SchemaType* additionalItemsSchema_;
1498 const SchemaType* itemsList_;
1499 const SchemaType** itemsTuple_;
1500 SizeType itemsTupleCount_;
1501 SizeType minItems_;
1502 SizeType maxItems_;
1503 bool additionalItems_;
1504 bool uniqueItems_;
1505
1506 RegexType* pattern_;
1507 SizeType minLength_;
1508 SizeType maxLength_;
1509
1510 SValue minimum_;
1511 SValue maximum_;
1512 SValue multipleOf_;
1513 bool exclusiveMinimum_;
1514 bool exclusiveMaximum_;
1515
1516 SizeType defaultValueLength_;
1517 };
1518
1519 template<typename Stack, typename Ch>
1520 struct TokenHelper {
AppendIndexTokenTokenHelper1521 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1522 *documentStack.template Push<Ch>() = '/';
1523 char buffer[21];
1524 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1525 for (size_t i = 0; i < length; i++)
1526 *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1527 }
1528 };
1529
1530 // Partial specialized version for char to prevent buffer copying.
1531 template <typename Stack>
1532 struct TokenHelper<Stack, char> {
1533 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1534 if (sizeof(SizeType) == 4) {
1535 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1536 *buffer++ = '/';
1537 const char* end = internal::u32toa(index, buffer);
1538 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1539 }
1540 else {
1541 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1542 *buffer++ = '/';
1543 const char* end = internal::u64toa(index, buffer);
1544 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1545 }
1546 }
1547 };
1548
1549 } // namespace internal
1550
1551 ///////////////////////////////////////////////////////////////////////////////
1552 // IGenericRemoteSchemaDocumentProvider
1553
1554 template <typename SchemaDocumentType>
1555 class IGenericRemoteSchemaDocumentProvider {
1556 public:
1557 typedef typename SchemaDocumentType::Ch Ch;
1558
1559 virtual ~IGenericRemoteSchemaDocumentProvider() {}
1560 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1561 };
1562
1563 ///////////////////////////////////////////////////////////////////////////////
1564 // GenericSchemaDocument
1565
1566 //! JSON schema document.
1567 /*!
1568 A JSON schema document is a compiled version of a JSON schema.
1569 It is basically a tree of internal::Schema.
1570
1571 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1572 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1573 \tparam Allocator Allocator type for allocating memory of this document.
1574 */
1575 template <typename ValueT, typename Allocator = CrtAllocator>
1576 class GenericSchemaDocument {
1577 public:
1578 typedef ValueT ValueType;
1579 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1580 typedef Allocator AllocatorType;
1581 typedef typename ValueType::EncodingType EncodingType;
1582 typedef typename EncodingType::Ch Ch;
1583 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1584 typedef GenericPointer<ValueType, Allocator> PointerType;
1585 typedef GenericValue<EncodingType, Allocator> URIType;
1586 friend class internal::Schema<GenericSchemaDocument>;
1587 template <typename, typename, typename>
1588 friend class GenericSchemaValidator;
1589
1590 //! Constructor.
1591 /*!
1592 Compile a JSON document into schema document.
1593
1594 \param document A JSON document as source.
1595 \param uri The base URI of this schema document for purposes of violation reporting.
1596 \param uriLength Length of \c name, in code points.
1597 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1598 \param allocator An optional allocator instance for allocating memory. Can be null.
1599 */
1600 explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
1601 IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1602 remoteProvider_(remoteProvider),
1603 allocator_(allocator),
1604 ownAllocator_(),
1605 root_(),
1606 typeless_(),
1607 schemaMap_(allocator, kInitialSchemaMapSize),
1608 schemaRef_(allocator, kInitialSchemaRefSize)
1609 {
1610 if (!allocator_)
1611 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1612
1613 Ch noUri[1] = {0};
1614 uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
1615
1616 typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1617 new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_);
1618
1619 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1620 // And call AddRefSchema() if there are $ref.
1621 CreateSchemaRecursive(&root_, PointerType(), document, document);
1622
1623 // Resolve $ref
1624 while (!schemaRef_.Empty()) {
1625 SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1626 if (const SchemaType* s = GetSchema(refEntry->target)) {
1627 if (refEntry->schema)
1628 *refEntry->schema = s;
1629
1630 // Create entry in map if not exist
1631 if (!GetSchema(refEntry->source)) {
1632 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1633 }
1634 }
1635 else if (refEntry->schema)
1636 *refEntry->schema = typeless_;
1637
1638 refEntry->~SchemaRefEntry();
1639 }
1640
1641 RAPIDJSON_ASSERT(root_ != 0);
1642
1643 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1644 }
1645
1646 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1647 //! Move constructor in C++11
1648 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1649 remoteProvider_(rhs.remoteProvider_),
1650 allocator_(rhs.allocator_),
1651 ownAllocator_(rhs.ownAllocator_),
1652 root_(rhs.root_),
1653 typeless_(rhs.typeless_),
1654 schemaMap_(std::move(rhs.schemaMap_)),
1655 schemaRef_(std::move(rhs.schemaRef_)),
1656 uri_(std::move(rhs.uri_))
1657 {
1658 rhs.remoteProvider_ = 0;
1659 rhs.allocator_ = 0;
1660 rhs.ownAllocator_ = 0;
1661 rhs.typeless_ = 0;
1662 }
1663 #endif
1664
1665 //! Destructor
1666 ~GenericSchemaDocument() {
1667 while (!schemaMap_.Empty())
1668 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1669
1670 if (typeless_) {
1671 typeless_->~SchemaType();
1672 Allocator::Free(typeless_);
1673 }
1674
1675 RAPIDJSON_DELETE(ownAllocator_);
1676 }
1677
1678 const URIType& GetURI() const { return uri_; }
1679
1680 //! Get the root schema.
1681 const SchemaType& GetRoot() const { return *root_; }
1682
1683 private:
1684 //! Prohibit copying
1685 GenericSchemaDocument(const GenericSchemaDocument&);
1686 //! Prohibit assignment
1687 GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1688
1689 struct SchemaRefEntry {
1690 SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1691 PointerType source;
1692 PointerType target;
1693 const SchemaType** schema;
1694 };
1695
1696 struct SchemaEntry {
1697 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1698 ~SchemaEntry() {
1699 if (owned) {
1700 schema->~SchemaType();
1701 Allocator::Free(schema);
1702 }
1703 }
1704 PointerType pointer;
1705 SchemaType* schema;
1706 bool owned;
1707 };
1708
1709 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1710 if (schema)
1711 *schema = typeless_;
1712
1713 if (v.GetType() == kObjectType) {
1714 const SchemaType* s = GetSchema(pointer);
1715 if (!s)
1716 CreateSchema(schema, pointer, v, document);
1717
1718 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1719 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1720 }
1721 else if (v.GetType() == kArrayType)
1722 for (SizeType i = 0; i < v.Size(); i++)
1723 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1724 }
1725
1726 void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1727 RAPIDJSON_ASSERT(pointer.IsValid());
1728 if (v.IsObject()) {
1729 if (!HandleRefSchema(pointer, schema, v, document)) {
1730 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1731 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1732 if (schema)
1733 *schema = s;
1734 }
1735 }
1736 }
1737
1738 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1739 static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1740 static const ValueType kRefValue(kRefString, 4);
1741
1742 typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1743 if (itr == v.MemberEnd())
1744 return false;
1745
1746 if (itr->value.IsString()) {
1747 SizeType len = itr->value.GetStringLength();
1748 if (len > 0) {
1749 const Ch* s = itr->value.GetString();
1750 SizeType i = 0;
1751 while (i < len && s[i] != '#') // Find the first #
1752 i++;
1753
1754 if (i > 0) { // Remote reference, resolve immediately
1755 if (remoteProvider_) {
1756 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
1757 PointerType pointer(&s[i], len - i, allocator_);
1758 if (pointer.IsValid()) {
1759 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1760 if (schema)
1761 *schema = sc;
1762 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(source, const_cast<SchemaType*>(sc), false, allocator_);
1763 return true;
1764 }
1765 }
1766 }
1767 }
1768 }
1769 else if (s[i] == '#') { // Local reference, defer resolution
1770 PointerType pointer(&s[i], len - i, allocator_);
1771 if (pointer.IsValid()) {
1772 if (const ValueType* nv = pointer.Get(document))
1773 if (HandleRefSchema(source, schema, *nv, document))
1774 return true;
1775
1776 new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1777 return true;
1778 }
1779 }
1780 }
1781 }
1782 return false;
1783 }
1784
1785 const SchemaType* GetSchema(const PointerType& pointer) const {
1786 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1787 if (pointer == target->pointer)
1788 return target->schema;
1789 return 0;
1790 }
1791
1792 PointerType GetPointer(const SchemaType* schema) const {
1793 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1794 if (schema == target->schema)
1795 return target->pointer;
1796 return PointerType();
1797 }
1798
1799 const SchemaType* GetTypeless() const { return typeless_; }
1800
1801 static const size_t kInitialSchemaMapSize = 64;
1802 static const size_t kInitialSchemaRefSize = 64;
1803
1804 IRemoteSchemaDocumentProviderType* remoteProvider_;
1805 Allocator *allocator_;
1806 Allocator *ownAllocator_;
1807 const SchemaType* root_; //!< Root schema.
1808 SchemaType* typeless_;
1809 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1810 internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1811 URIType uri_;
1812 };
1813
1814 //! GenericSchemaDocument using Value type.
1815 typedef GenericSchemaDocument<Value> SchemaDocument;
1816 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1817 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1818
1819 ///////////////////////////////////////////////////////////////////////////////
1820 // GenericSchemaValidator
1821
1822 //! JSON Schema Validator.
1823 /*!
1824 A SAX style JSON schema validator.
1825 It uses a \c GenericSchemaDocument to validate SAX events.
1826 It delegates the incoming SAX events to an output handler.
1827 The default output handler does nothing.
1828 It can be reused multiple times by calling \c Reset().
1829
1830 \tparam SchemaDocumentType Type of schema document.
1831 \tparam OutputHandler Type of output handler. Default handler does nothing.
1832 \tparam StateAllocator Allocator for storing the internal validation states.
1833 */
1834 template <
1835 typename SchemaDocumentType,
1836 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1837 typename StateAllocator = CrtAllocator>
1838 class GenericSchemaValidator :
1839 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1840 public internal::ISchemaValidator,
1841 public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
1842 public:
1843 typedef typename SchemaDocumentType::SchemaType SchemaType;
1844 typedef typename SchemaDocumentType::PointerType PointerType;
1845 typedef typename SchemaType::EncodingType EncodingType;
1846 typedef typename SchemaType::SValue SValue;
1847 typedef typename EncodingType::Ch Ch;
1848 typedef GenericStringRef<Ch> StringRefType;
1849 typedef GenericValue<EncodingType, StateAllocator> ValueType;
1850
1851 //! Constructor without output handler.
1852 /*!
1853 \param schemaDocument The schema document to conform to.
1854 \param allocator Optional allocator for storing internal validation states.
1855 \param schemaStackCapacity Optional initial capacity of schema path stack.
1856 \param documentStackCapacity Optional initial capacity of document path stack.
1857 */
1858 GenericSchemaValidator(
1859 const SchemaDocumentType& schemaDocument,
1860 StateAllocator* allocator = 0,
1861 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1862 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1863 :
1864 schemaDocument_(&schemaDocument),
1865 root_(schemaDocument.GetRoot()),
1866 stateAllocator_(allocator),
1867 ownStateAllocator_(0),
1868 schemaStack_(allocator, schemaStackCapacity),
1869 documentStack_(allocator, documentStackCapacity),
1870 outputHandler_(0),
1871 error_(kObjectType),
1872 currentError_(),
1873 missingDependents_(),
1874 valid_(true),
1875 flags_(kValidateDefaultFlags)
1876 #if RAPIDJSON_SCHEMA_VERBOSE
1877 , depth_(0)
1878 #endif
1879 {
1880 }
1881
1882 //! Constructor with output handler.
1883 /*!
1884 \param schemaDocument The schema document to conform to.
1885 \param allocator Optional allocator for storing internal validation states.
1886 \param schemaStackCapacity Optional initial capacity of schema path stack.
1887 \param documentStackCapacity Optional initial capacity of document path stack.
1888 */
1889 GenericSchemaValidator(
1890 const SchemaDocumentType& schemaDocument,
1891 OutputHandler& outputHandler,
1892 StateAllocator* allocator = 0,
1893 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1894 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1895 :
1896 schemaDocument_(&schemaDocument),
1897 root_(schemaDocument.GetRoot()),
1898 stateAllocator_(allocator),
1899 ownStateAllocator_(0),
1900 schemaStack_(allocator, schemaStackCapacity),
1901 documentStack_(allocator, documentStackCapacity),
1902 outputHandler_(&outputHandler),
1903 error_(kObjectType),
1904 currentError_(),
1905 missingDependents_(),
1906 valid_(true),
1907 flags_(kValidateDefaultFlags)
1908 #if RAPIDJSON_SCHEMA_VERBOSE
1909 , depth_(0)
1910 #endif
1911 {
1912 }
1913
1914 //! Destructor.
1915 ~GenericSchemaValidator() {
1916 Reset();
1917 RAPIDJSON_DELETE(ownStateAllocator_);
1918 }
1919
1920 //! Reset the internal states.
1921 void Reset() {
1922 while (!schemaStack_.Empty())
1923 PopSchema();
1924 documentStack_.Clear();
1925 ResetError();
1926 }
1927
1928 //! Reset the error state.
1929 void ResetError() {
1930 error_.SetObject();
1931 currentError_.SetNull();
1932 missingDependents_.SetNull();
1933 valid_ = true;
1934 }
1935
1936 //! Implementation of ISchemaValidator
1937 void SetValidateFlags(unsigned flags) {
1938 flags_ = flags;
1939 }
1940 virtual unsigned GetValidateFlags() const {
1941 return flags_;
1942 }
1943
1944 //! Checks whether the current state is valid.
1945 // Implementation of ISchemaValidator
1946 virtual bool IsValid() const {
1947 if (!valid_) return false;
1948 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
1949 return true;
1950 }
1951
1952 //! Gets the error object.
1953 ValueType& GetError() { return error_; }
1954 const ValueType& GetError() const { return error_; }
1955
1956 //! Gets the JSON pointer pointed to the invalid schema.
1957 // If reporting all errors, the stack will be empty.
1958 PointerType GetInvalidSchemaPointer() const {
1959 return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
1960 }
1961
1962 //! Gets the keyword of invalid schema.
1963 // If reporting all errors, the stack will be empty, so return "errors".
1964 const Ch* GetInvalidSchemaKeyword() const {
1965 if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
1966 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString();
1967 return 0;
1968 }
1969
1970 //! Gets the error code of invalid schema.
1971 // If reporting all errors, the stack will be empty, so return kValidateErrors.
1972 ValidateErrorCode GetInvalidSchemaCode() const {
1973 if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
1974 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
1975 return kValidateErrorNone;
1976 }
1977
1978 //! Gets the JSON pointer pointed to the invalid value.
1979 // If reporting all errors, the stack will be empty.
1980 PointerType GetInvalidDocumentPointer() const {
1981 if (documentStack_.Empty()) {
1982 return PointerType();
1983 }
1984 else {
1985 return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1986 }
1987 }
1988
1989 void NotMultipleOf(int64_t actual, const SValue& expected) {
1990 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
1991 }
1992 void NotMultipleOf(uint64_t actual, const SValue& expected) {
1993 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
1994 }
1995 void NotMultipleOf(double actual, const SValue& expected) {
1996 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
1997 }
1998 void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
1999 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2000 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2001 }
2002 void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
2003 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2004 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2005 }
2006 void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
2007 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2008 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2009 }
2010 void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
2011 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2012 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2013 }
2014 void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
2015 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2016 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2017 }
2018 void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
2019 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2020 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2021 }
2022
2023 void TooLong(const Ch* str, SizeType length, SizeType expected) {
2024 AddNumberError(kValidateErrorMaxLength,
2025 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
2026 }
2027 void TooShort(const Ch* str, SizeType length, SizeType expected) {
2028 AddNumberError(kValidateErrorMinLength,
2029 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
2030 }
2031 void DoesNotMatch(const Ch* str, SizeType length) {
2032 currentError_.SetObject();
2033 currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
2034 AddCurrentError(kValidateErrorPattern);
2035 }
2036
2037 void DisallowedItem(SizeType index) {
2038 currentError_.SetObject();
2039 currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
2040 AddCurrentError(kValidateErrorAdditionalItems, true);
2041 }
2042 void TooFewItems(SizeType actualCount, SizeType expectedCount) {
2043 AddNumberError(kValidateErrorMinItems,
2044 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2045 }
2046 void TooManyItems(SizeType actualCount, SizeType expectedCount) {
2047 AddNumberError(kValidateErrorMaxItems,
2048 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2049 }
2050 void DuplicateItems(SizeType index1, SizeType index2) {
2051 ValueType duplicates(kArrayType);
2052 duplicates.PushBack(index1, GetStateAllocator());
2053 duplicates.PushBack(index2, GetStateAllocator());
2054 currentError_.SetObject();
2055 currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
2056 AddCurrentError(kValidateErrorUniqueItems, true);
2057 }
2058
2059 void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
2060 AddNumberError(kValidateErrorMaxProperties,
2061 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2062 }
2063 void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
2064 AddNumberError(kValidateErrorMinProperties,
2065 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2066 }
2067 void StartMissingProperties() {
2068 currentError_.SetArray();
2069 }
2070 void AddMissingProperty(const SValue& name) {
2071 currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
2072 }
2073 bool EndMissingProperties() {
2074 if (currentError_.Empty())
2075 return false;
2076 ValueType error(kObjectType);
2077 error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
2078 currentError_ = error;
2079 AddCurrentError(kValidateErrorRequired);
2080 return true;
2081 }
2082 void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
2083 for (SizeType i = 0; i < count; ++i)
2084 MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2085 }
2086 void DisallowedProperty(const Ch* name, SizeType length) {
2087 currentError_.SetObject();
2088 currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
2089 AddCurrentError(kValidateErrorAdditionalProperties, true);
2090 }
2091
2092 void StartDependencyErrors() {
2093 currentError_.SetObject();
2094 }
2095 void StartMissingDependentProperties() {
2096 missingDependents_.SetArray();
2097 }
2098 void AddMissingDependentProperty(const SValue& targetName) {
2099 missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
2100 }
2101 void EndMissingDependentProperties(const SValue& sourceName) {
2102 if (!missingDependents_.Empty()) {
2103 // Create equivalent 'required' error
2104 ValueType error(kObjectType);
2105 ValidateErrorCode code = kValidateErrorRequired;
2106 error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
2107 AddErrorCode(error, code);
2108 AddErrorInstanceLocation(error, false);
2109 // When appending to a pointer ensure its allocator is used
2110 PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
2111 AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
2112 ValueType wrapper(kObjectType);
2113 wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
2114 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
2115 }
2116 }
2117 void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
2118 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
2119 static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
2120 }
2121 bool EndDependencyErrors() {
2122 if (currentError_.ObjectEmpty())
2123 return false;
2124 ValueType error(kObjectType);
2125 error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
2126 currentError_ = error;
2127 AddCurrentError(kValidateErrorDependencies);
2128 return true;
2129 }
2130
2131 void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
2132 currentError_.SetObject();
2133 AddCurrentError(code);
2134 }
2135 void StartDisallowedType() {
2136 currentError_.SetArray();
2137 }
2138 void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
2139 currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
2140 }
2141 void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
2142 ValueType error(kObjectType);
2143 error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
2144 error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
2145 currentError_ = error;
2146 AddCurrentError(kValidateErrorType);
2147 }
2148 void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
2149 // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
2150 AddErrorArray(kValidateErrorAllOf, subvalidators, count);
2151 //for (SizeType i = 0; i < count; ++i) {
2152 // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2153 //}
2154 }
2155 void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
2156 AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
2157 }
2158 void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) {
2159 AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count);
2160 }
2161 void Disallowed() {
2162 currentError_.SetObject();
2163 AddCurrentError(kValidateErrorNot);
2164 }
2165
2166 #define RAPIDJSON_STRING_(name, ...) \
2167 static const StringRefType& Get##name##String() {\
2168 static const Ch s[] = { __VA_ARGS__, '\0' };\
2169 static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
2170 return v;\
2171 }
2172
2173 RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
2174 RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
2175 RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
2176 RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
2177 RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
2178 RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
2179 RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
2180 RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
2181 RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
2182 RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
2183
2184 #undef RAPIDJSON_STRING_
2185
2186 #if RAPIDJSON_SCHEMA_VERBOSE
2187 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
2188 RAPIDJSON_MULTILINEMACRO_BEGIN\
2189 *documentStack_.template Push<Ch>() = '\0';\
2190 documentStack_.template Pop<Ch>(1);\
2191 internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
2192 RAPIDJSON_MULTILINEMACRO_END
2193 #else
2194 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
2195 #endif
2196
2197 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
2198 if (!valid_) return false; \
2199 if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
2200 RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
2201 return valid_ = false;\
2202 }
2203
2204 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
2205 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
2206 if (context->hasher)\
2207 static_cast<HasherType*>(context->hasher)->method arg2;\
2208 if (context->validators)\
2209 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
2210 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
2211 if (context->patternPropertiesValidators)\
2212 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
2213 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
2214 }
2215
2216 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
2217 valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
2218 return valid_;
2219
2220 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
2221 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
2222 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
2223 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
2224
2225 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); }
2226 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
2227 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
2228 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
2229 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
2230 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
2231 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
2232 bool RawNumber(const Ch* str, SizeType length, bool copy)
2233 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2234 bool String(const Ch* str, SizeType length, bool copy)
2235 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2236
2237 bool StartObject() {
2238 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
2239 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
2240 return valid_ = !outputHandler_ || outputHandler_->StartObject();
2241 }
2242
2243 bool Key(const Ch* str, SizeType len, bool copy) {
2244 if (!valid_) return false;
2245 AppendToken(str, len);
2246 if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false;
2247 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
2248 return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
2249 }
2250
2251 bool EndObject(SizeType memberCount) {
2252 if (!valid_) return false;
2253 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
2254 if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false;
2255 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
2256 }
2257
2258 bool StartArray() {
2259 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
2260 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
2261 return valid_ = !outputHandler_ || outputHandler_->StartArray();
2262 }
2263
2264 bool EndArray(SizeType elementCount) {
2265 if (!valid_) return false;
2266 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
2267 if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false;
2268 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
2269 }
2270
2271 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
2272 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
2273 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
2274 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
2275
2276 // Implementation of ISchemaStateFactory<SchemaType>
2277 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
2278 ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
2279 #if RAPIDJSON_SCHEMA_VERBOSE
2280 depth_ + 1,
2281 #endif
2282 &GetStateAllocator());
2283 sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag);
2284 return sv;
2285 }
2286
2287 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
2288 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
2289 v->~GenericSchemaValidator();
2290 StateAllocator::Free(v);
2291 }
2292
2293 virtual void* CreateHasher() {
2294 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
2295 }
2296
2297 virtual uint64_t GetHashCode(void* hasher) {
2298 return static_cast<HasherType*>(hasher)->GetHashCode();
2299 }
2300
2301 virtual void DestroryHasher(void* hasher) {
2302 HasherType* h = static_cast<HasherType*>(hasher);
2303 h->~HasherType();
2304 StateAllocator::Free(h);
2305 }
2306
2307 virtual void* MallocState(size_t size) {
2308 return GetStateAllocator().Malloc(size);
2309 }
2310
2311 virtual void FreeState(void* p) {
2312 StateAllocator::Free(p);
2313 }
2314
2315 private:
2316 typedef typename SchemaType::Context Context;
2317 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
2318 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
2319
2320 GenericSchemaValidator(
2321 const SchemaDocumentType& schemaDocument,
2322 const SchemaType& root,
2323 const char* basePath, size_t basePathSize,
2324 #if RAPIDJSON_SCHEMA_VERBOSE
2325 unsigned depth,
2326 #endif
2327 StateAllocator* allocator = 0,
2328 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2329 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2330 :
2331 schemaDocument_(&schemaDocument),
2332 root_(root),
2333 stateAllocator_(allocator),
2334 ownStateAllocator_(0),
2335 schemaStack_(allocator, schemaStackCapacity),
2336 documentStack_(allocator, documentStackCapacity),
2337 outputHandler_(0),
2338 error_(kObjectType),
2339 currentError_(),
2340 missingDependents_(),
2341 valid_(true),
2342 flags_(kValidateDefaultFlags)
2343 #if RAPIDJSON_SCHEMA_VERBOSE
2344 , depth_(depth)
2345 #endif
2346 {
2347 if (basePath && basePathSize)
2348 memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
2349 }
2350
2351 StateAllocator& GetStateAllocator() {
2352 if (!stateAllocator_)
2353 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
2354 return *stateAllocator_;
2355 }
2356
2357 bool GetContinueOnErrors() const {
2358 return flags_ & kValidateContinueOnErrorFlag;
2359 }
2360
2361 bool BeginValue() {
2362 if (schemaStack_.Empty())
2363 PushSchema(root_);
2364 else {
2365 if (CurrentContext().inArray)
2366 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
2367
2368 if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
2369 return false;
2370
2371 SizeType count = CurrentContext().patternPropertiesSchemaCount;
2372 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
2373 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
2374 bool valueUniqueness = CurrentContext().valueUniqueness;
2375 RAPIDJSON_ASSERT(CurrentContext().valueSchema);
2376 PushSchema(*CurrentContext().valueSchema);
2377
2378 if (count > 0) {
2379 CurrentContext().objectPatternValidatorType = patternValidatorType;
2380 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
2381 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
2382 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
2383 for (SizeType i = 0; i < count; i++)
2384 va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
2385 }
2386
2387 CurrentContext().arrayUniqueness = valueUniqueness;
2388 }
2389 return true;
2390 }
2391
2392 bool EndValue() {
2393 if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
2394 return false;
2395
2396 #if RAPIDJSON_SCHEMA_VERBOSE
2397 GenericStringBuffer<EncodingType> sb;
2398 schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
2399
2400 *documentStack_.template Push<Ch>() = '\0';
2401 documentStack_.template Pop<Ch>(1);
2402 internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
2403 #endif
2404 void* hasher = CurrentContext().hasher;
2405 uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
2406
2407 PopSchema();
2408
2409 if (!schemaStack_.Empty()) {
2410 Context& context = CurrentContext();
2411 // Only check uniqueness if there is a hasher
2412 if (hasher && context.valueUniqueness) {
2413 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
2414 if (!a)
2415 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
2416 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
2417 if (itr->GetUint64() == h) {
2418 DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
2419 // Cleanup before returning if continuing
2420 if (GetContinueOnErrors()) {
2421 a->PushBack(h, GetStateAllocator());
2422 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
2423 }
2424 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
2425 }
2426 a->PushBack(h, GetStateAllocator());
2427 }
2428 }
2429
2430 // Remove the last token of document pointer
2431 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
2432 ;
2433
2434 return true;
2435 }
2436
2437 void AppendToken(const Ch* str, SizeType len) {
2438 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
2439 *documentStack_.template PushUnsafe<Ch>() = '/';
2440 for (SizeType i = 0; i < len; i++) {
2441 if (str[i] == '~') {
2442 *documentStack_.template PushUnsafe<Ch>() = '~';
2443 *documentStack_.template PushUnsafe<Ch>() = '0';
2444 }
2445 else if (str[i] == '/') {
2446 *documentStack_.template PushUnsafe<Ch>() = '~';
2447 *documentStack_.template PushUnsafe<Ch>() = '1';
2448 }
2449 else
2450 *documentStack_.template PushUnsafe<Ch>() = str[i];
2451 }
2452 }
2453
2454 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema); }
2455
2456 RAPIDJSON_FORCEINLINE void PopSchema() {
2457 Context* c = schemaStack_.template Pop<Context>(1);
2458 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
2459 a->~HashCodeArray();
2460 StateAllocator::Free(a);
2461 }
2462 c->~Context();
2463 }
2464
2465 void AddErrorInstanceLocation(ValueType& result, bool parent) {
2466 GenericStringBuffer<EncodingType> sb;
2467 PointerType instancePointer = GetInvalidDocumentPointer();
2468 ((parent && instancePointer.GetTokenCount() > 0)
2469 ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
2470 : instancePointer).StringifyUriFragment(sb);
2471 ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
2472 GetStateAllocator());
2473 result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
2474 }
2475
2476 void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
2477 GenericStringBuffer<EncodingType> sb;
2478 SizeType len = CurrentSchema().GetURI().GetStringLength();
2479 if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
2480 if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
2481 else GetInvalidSchemaPointer().StringifyUriFragment(sb);
2482 ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
2483 GetStateAllocator());
2484 result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
2485 }
2486
2487 void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
2488 result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
2489 }
2490
2491 void AddError(ValueType& keyword, ValueType& error) {
2492 typename ValueType::MemberIterator member = error_.FindMember(keyword);
2493 if (member == error_.MemberEnd())
2494 error_.AddMember(keyword, error, GetStateAllocator());
2495 else {
2496 if (member->value.IsObject()) {
2497 ValueType errors(kArrayType);
2498 errors.PushBack(member->value, GetStateAllocator());
2499 member->value = errors;
2500 }
2501 member->value.PushBack(error, GetStateAllocator());
2502 }
2503 }
2504
2505 void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
2506 AddErrorCode(currentError_, code);
2507 AddErrorInstanceLocation(currentError_, parent);
2508 AddErrorSchemaLocation(currentError_);
2509 AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
2510 }
2511
2512 void MergeError(ValueType& other) {
2513 for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
2514 AddError(it->name, it->value);
2515 }
2516 }
2517
2518 void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
2519 const typename SchemaType::ValueType& (*exclusive)() = 0) {
2520 currentError_.SetObject();
2521 currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
2522 currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
2523 if (exclusive)
2524 currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
2525 AddCurrentError(code);
2526 }
2527
2528 void AddErrorArray(const ValidateErrorCode code,
2529 ISchemaValidator** subvalidators, SizeType count) {
2530 ValueType errors(kArrayType);
2531 for (SizeType i = 0; i < count; ++i)
2532 errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
2533 currentError_.SetObject();
2534 currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
2535 AddCurrentError(code);
2536 }
2537
2538 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
2539 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
2540 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
2541
2542 static const size_t kDefaultSchemaStackCapacity = 1024;
2543 static const size_t kDefaultDocumentStackCapacity = 256;
2544 const SchemaDocumentType* schemaDocument_;
2545 const SchemaType& root_;
2546 StateAllocator* stateAllocator_;
2547 StateAllocator* ownStateAllocator_;
2548 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
2549 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
2550 OutputHandler* outputHandler_;
2551 ValueType error_;
2552 ValueType currentError_;
2553 ValueType missingDependents_;
2554 bool valid_;
2555 unsigned flags_;
2556 #if RAPIDJSON_SCHEMA_VERBOSE
2557 unsigned depth_;
2558 #endif
2559 };
2560
2561 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
2562
2563 ///////////////////////////////////////////////////////////////////////////////
2564 // SchemaValidatingReader
2565
2566 //! A helper class for parsing with validation.
2567 /*!
2568 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
2569
2570 \tparam parseFlags Combination of \ref ParseFlag.
2571 \tparam InputStream Type of input stream, implementing Stream concept.
2572 \tparam SourceEncoding Encoding of the input stream.
2573 \tparam SchemaDocumentType Type of schema document.
2574 \tparam StackAllocator Allocator type for stack.
2575 */
2576 template <
2577 unsigned parseFlags,
2578 typename InputStream,
2579 typename SourceEncoding,
2580 typename SchemaDocumentType = SchemaDocument,
2581 typename StackAllocator = CrtAllocator>
2582 class SchemaValidatingReader {
2583 public:
2584 typedef typename SchemaDocumentType::PointerType PointerType;
2585 typedef typename InputStream::Ch Ch;
2586 typedef GenericValue<SourceEncoding, StackAllocator> ValueType;
2587
2588 //! Constructor
2589 /*!
2590 \param is Input stream.
2591 \param sd Schema document.
2592 */
2593 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
2594
2595 template <typename Handler>
2596 bool operator()(Handler& handler) {
2597 GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
2598 GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
2599 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
2600
2601 isValid_ = validator.IsValid();
2602 if (isValid_) {
2603 invalidSchemaPointer_ = PointerType();
2604 invalidSchemaKeyword_ = 0;
2605 invalidDocumentPointer_ = PointerType();
2606 error_.SetObject();
2607 }
2608 else {
2609 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
2610 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
2611 invalidSchemaCode_ = validator.GetInvalidSchemaCode();
2612 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
2613 error_.CopyFrom(validator.GetError(), allocator_);
2614 }
2615
2616 return parseResult_;
2617 }
2618
2619 const ParseResult& GetParseResult() const { return parseResult_; }
2620 bool IsValid() const { return isValid_; }
2621 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
2622 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
2623 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
2624 const ValueType& GetError() const { return error_; }
2625 ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
2626
2627 private:
2628 InputStream& is_;
2629 const SchemaDocumentType& sd_;
2630
2631 ParseResult parseResult_;
2632 PointerType invalidSchemaPointer_;
2633 const Ch* invalidSchemaKeyword_;
2634 PointerType invalidDocumentPointer_;
2635 ValidateErrorCode invalidSchemaCode_;
2636 StackAllocator allocator_;
2637 ValueType error_;
2638 bool isValid_;
2639 };
2640
2641 RAPIDJSON_NAMESPACE_END
2642 RAPIDJSON_DIAG_POP
2643
2644 #endif // RAPIDJSON_SCHEMA_H_
2645