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 <cmath> // abs, floor
21
22 #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
23 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
24 #else
25 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
26 #endif
27
28 #if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
29 #define RAPIDJSON_SCHEMA_USE_STDREGEX 1
30 #else
31 #define RAPIDJSON_SCHEMA_USE_STDREGEX 0
32 #endif
33
34 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
35 #include "internal/regex.h"
36 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
37 #include <regex>
38 #endif
39
40 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
41 #define RAPIDJSON_SCHEMA_HAS_REGEX 1
42 #else
43 #define RAPIDJSON_SCHEMA_HAS_REGEX 0
44 #endif
45
46 #ifndef RAPIDJSON_SCHEMA_VERBOSE
47 #define RAPIDJSON_SCHEMA_VERBOSE 0
48 #endif
49
50 #if RAPIDJSON_SCHEMA_VERBOSE
51 #include "stringbuffer.h"
52 #endif
53
54 RAPIDJSON_DIAG_PUSH
55
56 #if defined(__GNUC__)
57 RAPIDJSON_DIAG_OFF(effc++)
58 #endif
59
60 #ifdef __clang__
61 RAPIDJSON_DIAG_OFF(weak-vtables)
62 RAPIDJSON_DIAG_OFF(exit-time-destructors)
63 RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
64 RAPIDJSON_DIAG_OFF(variadic-macros)
65 #endif
66
67 #ifdef _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(keyword)\
118 RAPIDJSON_MULTILINEMACRO_BEGIN\
119 context.invalidKeyword = keyword.GetString();\
120 RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
121 return false;\
122 RAPIDJSON_MULTILINEMACRO_END
123
124 ///////////////////////////////////////////////////////////////////////////////
125 // Forward declarations
126
127 template <typename ValueType, typename Allocator>
128 class GenericSchemaDocument;
129
130 namespace internal {
131
132 template <typename SchemaDocumentType>
133 class Schema;
134
135 ///////////////////////////////////////////////////////////////////////////////
136 // ISchemaValidator
137
138 class ISchemaValidator {
139 public:
~ISchemaValidator()140 virtual ~ISchemaValidator() {}
141 virtual bool IsValid() const = 0;
142 };
143
144 ///////////////////////////////////////////////////////////////////////////////
145 // ISchemaStateFactory
146
147 template <typename SchemaType>
148 class ISchemaStateFactory {
149 public:
~ISchemaStateFactory()150 virtual ~ISchemaStateFactory() {}
151 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
152 virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
153 virtual void* CreateHasher() = 0;
154 virtual uint64_t GetHashCode(void* hasher) = 0;
155 virtual void DestroryHasher(void* hasher) = 0;
156 virtual void* MallocState(size_t size) = 0;
157 virtual void FreeState(void* p) = 0;
158 };
159
160 ///////////////////////////////////////////////////////////////////////////////
161 // Hasher
162
163 // For comparison of compound value
164 template<typename Encoding, typename Allocator>
165 class Hasher {
166 public:
167 typedef typename Encoding::Ch Ch;
168
stack_(allocator,stackCapacity)169 Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
170
Null()171 bool Null() { return WriteType(kNullType); }
Bool(bool b)172 bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
Int(int i)173 bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
Uint(unsigned u)174 bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
Int64(int64_t i)175 bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
Uint64(uint64_t u)176 bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
Double(double d)177 bool Double(double d) {
178 Number n;
179 if (d < 0) n.u.i = static_cast<int64_t>(d);
180 else n.u.u = static_cast<uint64_t>(d);
181 n.d = d;
182 return WriteNumber(n);
183 }
184
RawNumber(const Ch * str,SizeType len,bool)185 bool RawNumber(const Ch* str, SizeType len, bool) {
186 WriteBuffer(kNumberType, str, len * sizeof(Ch));
187 return true;
188 }
189
String(const Ch * str,SizeType len,bool)190 bool String(const Ch* str, SizeType len, bool) {
191 WriteBuffer(kStringType, str, len * sizeof(Ch));
192 return true;
193 }
194
StartObject()195 bool StartObject() { return true; }
Key(const Ch * str,SizeType len,bool copy)196 bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
EndObject(SizeType memberCount)197 bool EndObject(SizeType memberCount) {
198 uint64_t h = Hash(0, kObjectType);
199 uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
200 for (SizeType i = 0; i < memberCount; i++)
201 h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
202 *stack_.template Push<uint64_t>() = h;
203 return true;
204 }
205
StartArray()206 bool StartArray() { return true; }
EndArray(SizeType elementCount)207 bool EndArray(SizeType elementCount) {
208 uint64_t h = Hash(0, kArrayType);
209 uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
210 for (SizeType i = 0; i < elementCount; i++)
211 h = Hash(h, e[i]); // Use hash to achieve element order sensitive
212 *stack_.template Push<uint64_t>() = h;
213 return true;
214 }
215
IsValid()216 bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
217
GetHashCode()218 uint64_t GetHashCode() const {
219 RAPIDJSON_ASSERT(IsValid());
220 return *stack_.template Top<uint64_t>();
221 }
222
223 private:
224 static const size_t kDefaultSize = 256;
225 struct Number {
226 union U {
227 uint64_t u;
228 int64_t i;
229 }u;
230 double d;
231 };
232
WriteType(Type type)233 bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
234
WriteNumber(const Number & n)235 bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
236
WriteBuffer(Type type,const void * data,size_t len)237 bool WriteBuffer(Type type, const void* data, size_t len) {
238 // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
239 uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
240 const unsigned char* d = static_cast<const unsigned char*>(data);
241 for (size_t i = 0; i < len; i++)
242 h = Hash(h, d[i]);
243 *stack_.template Push<uint64_t>() = h;
244 return true;
245 }
246
Hash(uint64_t h,uint64_t d)247 static uint64_t Hash(uint64_t h, uint64_t d) {
248 static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
249 h ^= d;
250 h *= kPrime;
251 return h;
252 }
253
254 Stack<Allocator> stack_;
255 };
256
257 ///////////////////////////////////////////////////////////////////////////////
258 // SchemaValidationContext
259
260 template <typename SchemaDocumentType>
261 struct SchemaValidationContext {
262 typedef Schema<SchemaDocumentType> SchemaType;
263 typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
264 typedef typename SchemaType::ValueType ValueType;
265 typedef typename ValueType::Ch Ch;
266
267 enum PatternValidatorType {
268 kPatternValidatorOnly,
269 kPatternValidatorWithProperty,
270 kPatternValidatorWithAdditionalProperty
271 };
272
SchemaValidationContextSchemaValidationContext273 SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
274 factory(f),
275 schema(s),
276 valueSchema(),
277 invalidKeyword(),
278 hasher(),
279 arrayElementHashCodes(),
280 validators(),
281 validatorCount(),
282 patternPropertiesValidators(),
283 patternPropertiesValidatorCount(),
284 patternPropertiesSchemas(),
285 patternPropertiesSchemaCount(),
286 valuePatternValidatorType(kPatternValidatorOnly),
287 propertyExist(),
288 inArray(false),
289 valueUniqueness(false),
290 arrayUniqueness(false)
291 {
292 }
293
~SchemaValidationContextSchemaValidationContext294 ~SchemaValidationContext() {
295 if (hasher)
296 factory.DestroryHasher(hasher);
297 if (validators) {
298 for (SizeType i = 0; i < validatorCount; i++)
299 factory.DestroySchemaValidator(validators[i]);
300 factory.FreeState(validators);
301 }
302 if (patternPropertiesValidators) {
303 for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
304 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
305 factory.FreeState(patternPropertiesValidators);
306 }
307 if (patternPropertiesSchemas)
308 factory.FreeState(patternPropertiesSchemas);
309 if (propertyExist)
310 factory.FreeState(propertyExist);
311 }
312
313 SchemaValidatorFactoryType& factory;
314 const SchemaType* schema;
315 const SchemaType* valueSchema;
316 const Ch* invalidKeyword;
317 void* hasher; // Only validator access
318 void* arrayElementHashCodes; // Only validator access this
319 ISchemaValidator** validators;
320 SizeType validatorCount;
321 ISchemaValidator** patternPropertiesValidators;
322 SizeType patternPropertiesValidatorCount;
323 const SchemaType** patternPropertiesSchemas;
324 SizeType patternPropertiesSchemaCount;
325 PatternValidatorType valuePatternValidatorType;
326 PatternValidatorType objectPatternValidatorType;
327 SizeType arrayElementIndex;
328 bool* propertyExist;
329 bool inArray;
330 bool valueUniqueness;
331 bool arrayUniqueness;
332 };
333
334 ///////////////////////////////////////////////////////////////////////////////
335 // Schema
336
337 template <typename SchemaDocumentType>
338 class Schema {
339 public:
340 typedef typename SchemaDocumentType::ValueType ValueType;
341 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
342 typedef typename SchemaDocumentType::PointerType PointerType;
343 typedef typename ValueType::EncodingType EncodingType;
344 typedef typename EncodingType::Ch Ch;
345 typedef SchemaValidationContext<SchemaDocumentType> Context;
346 typedef Schema<SchemaDocumentType> SchemaType;
347 typedef GenericValue<EncodingType, AllocatorType> SValue;
348 friend class GenericSchemaDocument<ValueType, AllocatorType>;
349
Schema(SchemaDocumentType * schemaDocument,const PointerType & p,const ValueType & value,const ValueType & document,AllocatorType * allocator)350 Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
351 allocator_(allocator),
352 typeless_(schemaDocument->GetTypeless()),
353 enum_(),
354 enumCount_(),
355 not_(),
356 type_((1 << kTotalSchemaType) - 1), // typeless
357 validatorCount_(),
358 properties_(),
359 additionalPropertiesSchema_(),
360 patternProperties_(),
361 patternPropertyCount_(),
362 propertyCount_(),
363 minProperties_(),
364 maxProperties_(SizeType(~0)),
365 additionalProperties_(true),
366 hasDependencies_(),
367 hasRequired_(),
368 hasSchemaDependencies_(),
369 additionalItemsSchema_(),
370 itemsList_(),
371 itemsTuple_(),
372 itemsTupleCount_(),
373 minItems_(),
374 maxItems_(SizeType(~0)),
375 additionalItems_(true),
376 uniqueItems_(false),
377 pattern_(),
378 minLength_(0),
379 maxLength_(~SizeType(0)),
380 exclusiveMinimum_(false),
381 exclusiveMaximum_(false)
382 {
383 typedef typename SchemaDocumentType::ValueType ValueType;
384 typedef typename ValueType::ConstValueIterator ConstValueIterator;
385 typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
386
387 if (!value.IsObject())
388 return;
389
390 if (const ValueType* v = GetMember(value, GetTypeString())) {
391 type_ = 0;
392 if (v->IsString())
393 AddType(*v);
394 else if (v->IsArray())
395 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
396 AddType(*itr);
397 }
398
399 if (const ValueType* v = GetMember(value, GetEnumString()))
400 if (v->IsArray() && v->Size() > 0) {
401 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
402 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
403 typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
404 char buffer[256 + 24];
405 MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
406 EnumHasherType h(&hasherAllocator, 256);
407 itr->Accept(h);
408 enum_[enumCount_++] = h.GetHashCode();
409 }
410 }
411
412 if (schemaDocument) {
413 AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
414 AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
415 AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
416 }
417
418 if (const ValueType* v = GetMember(value, GetNotString())) {
419 schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document);
420 notValidatorIndex_ = validatorCount_;
421 validatorCount_++;
422 }
423
424 // Object
425
426 const ValueType* properties = GetMember(value, GetPropertiesString());
427 const ValueType* required = GetMember(value, GetRequiredString());
428 const ValueType* dependencies = GetMember(value, GetDependenciesString());
429 {
430 // Gather properties from properties/required/dependencies
431 SValue allProperties(kArrayType);
432
433 if (properties && properties->IsObject())
434 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
435 AddUniqueElement(allProperties, itr->name);
436
437 if (required && required->IsArray())
438 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
439 if (itr->IsString())
440 AddUniqueElement(allProperties, *itr);
441
442 if (dependencies && dependencies->IsObject())
443 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
444 AddUniqueElement(allProperties, itr->name);
445 if (itr->value.IsArray())
446 for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
447 if (i->IsString())
448 AddUniqueElement(allProperties, *i);
449 }
450
451 if (allProperties.Size() > 0) {
452 propertyCount_ = allProperties.Size();
453 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
454 for (SizeType i = 0; i < propertyCount_; i++) {
455 new (&properties_[i]) Property();
456 properties_[i].name = allProperties[i];
457 properties_[i].schema = typeless_;
458 }
459 }
460 }
461
462 if (properties && properties->IsObject()) {
463 PointerType q = p.Append(GetPropertiesString(), allocator_);
464 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
465 SizeType index;
466 if (FindPropertyIndex(itr->name, &index))
467 schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
468 }
469 }
470
471 if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
472 PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
473 patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
474 patternPropertyCount_ = 0;
475
476 for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
477 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
478 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
479 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
480 patternPropertyCount_++;
481 }
482 }
483
484 if (required && required->IsArray())
485 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
486 if (itr->IsString()) {
487 SizeType index;
488 if (FindPropertyIndex(*itr, &index)) {
489 properties_[index].required = true;
490 hasRequired_ = true;
491 }
492 }
493
494 if (dependencies && dependencies->IsObject()) {
495 PointerType q = p.Append(GetDependenciesString(), allocator_);
496 hasDependencies_ = true;
497 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
498 SizeType sourceIndex;
499 if (FindPropertyIndex(itr->name, &sourceIndex)) {
500 if (itr->value.IsArray()) {
501 properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
502 std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
503 for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
504 SizeType targetIndex;
505 if (FindPropertyIndex(*targetItr, &targetIndex))
506 properties_[sourceIndex].dependencies[targetIndex] = true;
507 }
508 }
509 else if (itr->value.IsObject()) {
510 hasSchemaDependencies_ = true;
511 schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
512 properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
513 validatorCount_++;
514 }
515 }
516 }
517 }
518
519 if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
520 if (v->IsBool())
521 additionalProperties_ = v->GetBool();
522 else if (v->IsObject())
523 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
524 }
525
526 AssignIfExist(minProperties_, value, GetMinPropertiesString());
527 AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
528
529 // Array
530 if (const ValueType* v = GetMember(value, GetItemsString())) {
531 PointerType q = p.Append(GetItemsString(), allocator_);
532 if (v->IsObject()) // List validation
533 schemaDocument->CreateSchema(&itemsList_, q, *v, document);
534 else if (v->IsArray()) { // Tuple validation
535 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
536 SizeType index = 0;
537 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
538 schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
539 }
540 }
541
542 AssignIfExist(minItems_, value, GetMinItemsString());
543 AssignIfExist(maxItems_, value, GetMaxItemsString());
544
545 if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
546 if (v->IsBool())
547 additionalItems_ = v->GetBool();
548 else if (v->IsObject())
549 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
550 }
551
552 AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
553
554 // String
555 AssignIfExist(minLength_, value, GetMinLengthString());
556 AssignIfExist(maxLength_, value, GetMaxLengthString());
557
558 if (const ValueType* v = GetMember(value, GetPatternString()))
559 pattern_ = CreatePattern(*v);
560
561 // Number
562 if (const ValueType* v = GetMember(value, GetMinimumString()))
563 if (v->IsNumber())
564 minimum_.CopyFrom(*v, *allocator_);
565
566 if (const ValueType* v = GetMember(value, GetMaximumString()))
567 if (v->IsNumber())
568 maximum_.CopyFrom(*v, *allocator_);
569
570 AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
571 AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
572
573 if (const ValueType* v = GetMember(value, GetMultipleOfString()))
574 if (v->IsNumber() && v->GetDouble() > 0.0)
575 multipleOf_.CopyFrom(*v, *allocator_);
576 }
577
~Schema()578 ~Schema() {
579 AllocatorType::Free(enum_);
580 if (properties_) {
581 for (SizeType i = 0; i < propertyCount_; i++)
582 properties_[i].~Property();
583 AllocatorType::Free(properties_);
584 }
585 if (patternProperties_) {
586 for (SizeType i = 0; i < patternPropertyCount_; i++)
587 patternProperties_[i].~PatternProperty();
588 AllocatorType::Free(patternProperties_);
589 }
590 AllocatorType::Free(itemsTuple_);
591 #if RAPIDJSON_SCHEMA_HAS_REGEX
592 if (pattern_) {
593 pattern_->~RegexType();
594 AllocatorType::Free(pattern_);
595 }
596 #endif
597 }
598
BeginValue(Context & context)599 bool BeginValue(Context& context) const {
600 if (context.inArray) {
601 if (uniqueItems_)
602 context.valueUniqueness = true;
603
604 if (itemsList_)
605 context.valueSchema = itemsList_;
606 else if (itemsTuple_) {
607 if (context.arrayElementIndex < itemsTupleCount_)
608 context.valueSchema = itemsTuple_[context.arrayElementIndex];
609 else if (additionalItemsSchema_)
610 context.valueSchema = additionalItemsSchema_;
611 else if (additionalItems_)
612 context.valueSchema = typeless_;
613 else
614 RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
615 }
616 else
617 context.valueSchema = typeless_;
618
619 context.arrayElementIndex++;
620 }
621 return true;
622 }
623
EndValue(Context & context)624 RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
625 if (context.patternPropertiesValidatorCount > 0) {
626 bool otherValid = false;
627 SizeType count = context.patternPropertiesValidatorCount;
628 if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
629 otherValid = context.patternPropertiesValidators[--count]->IsValid();
630
631 bool patternValid = true;
632 for (SizeType i = 0; i < count; i++)
633 if (!context.patternPropertiesValidators[i]->IsValid()) {
634 patternValid = false;
635 break;
636 }
637
638 if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
639 if (!patternValid)
640 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
641 }
642 else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
643 if (!patternValid || !otherValid)
644 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
645 }
646 else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
647 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
648 }
649
650 if (enum_) {
651 const uint64_t h = context.factory.GetHashCode(context.hasher);
652 for (SizeType i = 0; i < enumCount_; i++)
653 if (enum_[i] == h)
654 goto foundEnum;
655 RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
656 foundEnum:;
657 }
658
659 if (allOf_.schemas)
660 for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
661 if (!context.validators[i]->IsValid())
662 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
663
664 if (anyOf_.schemas) {
665 for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
666 if (context.validators[i]->IsValid())
667 goto foundAny;
668 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
669 foundAny:;
670 }
671
672 if (oneOf_.schemas) {
673 bool oneValid = false;
674 for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
675 if (context.validators[i]->IsValid()) {
676 if (oneValid)
677 RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
678 else
679 oneValid = true;
680 }
681 if (!oneValid)
682 RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
683 }
684
685 if (not_ && context.validators[notValidatorIndex_]->IsValid())
686 RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
687
688 return true;
689 }
690
Null(Context & context)691 bool Null(Context& context) const {
692 if (!(type_ & (1 << kNullSchemaType)))
693 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
694 return CreateParallelValidator(context);
695 }
696
Bool(Context & context,bool)697 bool Bool(Context& context, bool) const {
698 if (!(type_ & (1 << kBooleanSchemaType)))
699 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
700 return CreateParallelValidator(context);
701 }
702
Int(Context & context,int i)703 bool Int(Context& context, int i) const {
704 if (!CheckInt(context, i))
705 return false;
706 return CreateParallelValidator(context);
707 }
708
Uint(Context & context,unsigned u)709 bool Uint(Context& context, unsigned u) const {
710 if (!CheckUint(context, u))
711 return false;
712 return CreateParallelValidator(context);
713 }
714
Int64(Context & context,int64_t i)715 bool Int64(Context& context, int64_t i) const {
716 if (!CheckInt(context, i))
717 return false;
718 return CreateParallelValidator(context);
719 }
720
Uint64(Context & context,uint64_t u)721 bool Uint64(Context& context, uint64_t u) const {
722 if (!CheckUint(context, u))
723 return false;
724 return CreateParallelValidator(context);
725 }
726
Double(Context & context,double d)727 bool Double(Context& context, double d) const {
728 if (!(type_ & (1 << kNumberSchemaType)))
729 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
730
731 if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
732 return false;
733
734 if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
735 return false;
736
737 if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
738 return false;
739
740 return CreateParallelValidator(context);
741 }
742
String(Context & context,const Ch * str,SizeType length,bool)743 bool String(Context& context, const Ch* str, SizeType length, bool) const {
744 if (!(type_ & (1 << kStringSchemaType)))
745 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
746
747 if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
748 SizeType count;
749 if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
750 if (count < minLength_)
751 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
752 if (count > maxLength_)
753 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
754 }
755 }
756
757 if (pattern_ && !IsPatternMatch(pattern_, str, length))
758 RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
759
760 return CreateParallelValidator(context);
761 }
762
StartObject(Context & context)763 bool StartObject(Context& context) const {
764 if (!(type_ & (1 << kObjectSchemaType)))
765 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
766
767 if (hasDependencies_ || hasRequired_) {
768 context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
769 std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
770 }
771
772 if (patternProperties_) { // pre-allocate schema array
773 SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
774 context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
775 context.patternPropertiesSchemaCount = 0;
776 std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
777 }
778
779 return CreateParallelValidator(context);
780 }
781
Key(Context & context,const Ch * str,SizeType len,bool)782 bool Key(Context& context, const Ch* str, SizeType len, bool) const {
783 if (patternProperties_) {
784 context.patternPropertiesSchemaCount = 0;
785 for (SizeType i = 0; i < patternPropertyCount_; i++)
786 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
787 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
788 context.valueSchema = typeless_;
789 }
790 }
791
792 SizeType index;
793 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
794 if (context.patternPropertiesSchemaCount > 0) {
795 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
796 context.valueSchema = typeless_;
797 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
798 }
799 else
800 context.valueSchema = properties_[index].schema;
801
802 if (context.propertyExist)
803 context.propertyExist[index] = true;
804
805 return true;
806 }
807
808 if (additionalPropertiesSchema_) {
809 if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
810 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
811 context.valueSchema = typeless_;
812 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
813 }
814 else
815 context.valueSchema = additionalPropertiesSchema_;
816 return true;
817 }
818 else if (additionalProperties_) {
819 context.valueSchema = typeless_;
820 return true;
821 }
822
823 if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
824 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
825
826 return true;
827 }
828
EndObject(Context & context,SizeType memberCount)829 bool EndObject(Context& context, SizeType memberCount) const {
830 if (hasRequired_)
831 for (SizeType index = 0; index < propertyCount_; index++)
832 if (properties_[index].required)
833 if (!context.propertyExist[index])
834 RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
835
836 if (memberCount < minProperties_)
837 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
838
839 if (memberCount > maxProperties_)
840 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
841
842 if (hasDependencies_) {
843 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
844 if (context.propertyExist[sourceIndex]) {
845 if (properties_[sourceIndex].dependencies) {
846 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
847 if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
848 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
849 }
850 else if (properties_[sourceIndex].dependenciesSchema)
851 if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
852 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
853 }
854 }
855
856 return true;
857 }
858
StartArray(Context & context)859 bool StartArray(Context& context) const {
860 if (!(type_ & (1 << kArraySchemaType)))
861 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
862
863 context.arrayElementIndex = 0;
864 context.inArray = true;
865
866 return CreateParallelValidator(context);
867 }
868
EndArray(Context & context,SizeType elementCount)869 bool EndArray(Context& context, SizeType elementCount) const {
870 context.inArray = false;
871
872 if (elementCount < minItems_)
873 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
874
875 if (elementCount > maxItems_)
876 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
877
878 return true;
879 }
880
881 // Generate functions for string literal according to Ch
882 #define RAPIDJSON_STRING_(name, ...) \
883 static const ValueType& Get##name##String() {\
884 static const Ch s[] = { __VA_ARGS__, '\0' };\
885 static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
886 return v;\
887 }
888
889 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
890 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
891 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
892 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
893 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
894 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
895 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
896 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
897 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
898 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
899 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
900 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
901 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
902 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
903 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
904 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
905 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
906 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
907 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
908 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
909 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
910 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
911 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
912 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
913 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
914 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
915 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
916 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
917 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
918 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
919 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
920 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
921 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
922
923 #undef RAPIDJSON_STRING_
924
925 private:
926 enum SchemaValueType {
927 kNullSchemaType,
928 kBooleanSchemaType,
929 kObjectSchemaType,
930 kArraySchemaType,
931 kStringSchemaType,
932 kNumberSchemaType,
933 kIntegerSchemaType,
934 kTotalSchemaType
935 };
936
937 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
938 typedef internal::GenericRegex<EncodingType> RegexType;
939 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
940 typedef std::basic_regex<Ch> RegexType;
941 #else
942 typedef char RegexType;
943 #endif
944
945 struct SchemaArray {
SchemaArraySchemaArray946 SchemaArray() : schemas(), count() {}
~SchemaArraySchemaArray947 ~SchemaArray() { AllocatorType::Free(schemas); }
948 const SchemaType** schemas;
949 SizeType begin; // begin index of context.validators
950 SizeType count;
951 };
952
953 template <typename V1, typename V2>
AddUniqueElement(V1 & a,const V2 & v)954 void AddUniqueElement(V1& a, const V2& v) {
955 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
956 if (*itr == v)
957 return;
958 V1 c(v, *allocator_);
959 a.PushBack(c, *allocator_);
960 }
961
GetMember(const ValueType & value,const ValueType & name)962 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
963 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
964 return itr != value.MemberEnd() ? &(itr->value) : 0;
965 }
966
AssignIfExist(bool & out,const ValueType & value,const ValueType & name)967 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
968 if (const ValueType* v = GetMember(value, name))
969 if (v->IsBool())
970 out = v->GetBool();
971 }
972
AssignIfExist(SizeType & out,const ValueType & value,const ValueType & name)973 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
974 if (const ValueType* v = GetMember(value, name))
975 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
976 out = static_cast<SizeType>(v->GetUint64());
977 }
978
AssignIfExist(SchemaArray & out,SchemaDocumentType & schemaDocument,const PointerType & p,const ValueType & value,const ValueType & name,const ValueType & document)979 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
980 if (const ValueType* v = GetMember(value, name)) {
981 if (v->IsArray() && v->Size() > 0) {
982 PointerType q = p.Append(name, allocator_);
983 out.count = v->Size();
984 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
985 memset(out.schemas, 0, sizeof(Schema*)* out.count);
986 for (SizeType i = 0; i < out.count; i++)
987 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
988 out.begin = validatorCount_;
989 validatorCount_ += out.count;
990 }
991 }
992 }
993
994 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
995 template <typename ValueType>
CreatePattern(const ValueType & value)996 RegexType* CreatePattern(const ValueType& value) {
997 if (value.IsString()) {
998 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
999 if (!r->IsValid()) {
1000 r->~RegexType();
1001 AllocatorType::Free(r);
1002 r = 0;
1003 }
1004 return r;
1005 }
1006 return 0;
1007 }
1008
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType)1009 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1010 GenericRegexSearch<RegexType> rs(*pattern);
1011 return rs.Search(str);
1012 }
1013 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1014 template <typename ValueType>
CreatePattern(const ValueType & value)1015 RegexType* CreatePattern(const ValueType& value) {
1016 if (value.IsString())
1017 try {
1018 return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1019 }
1020 catch (const std::regex_error&) {
1021 }
1022 return 0;
1023 }
1024
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType length)1025 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1026 std::match_results<const Ch*> r;
1027 return std::regex_search(str, str + length, r, *pattern);
1028 }
1029 #else
1030 template <typename ValueType>
CreatePattern(const ValueType &)1031 RegexType* CreatePattern(const ValueType&) { return 0; }
1032
IsPatternMatch(const RegexType *,const Ch *,SizeType)1033 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1034 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1035
AddType(const ValueType & type)1036 void AddType(const ValueType& type) {
1037 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1038 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1039 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1040 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1041 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1042 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1043 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1044 }
1045
CreateParallelValidator(Context & context)1046 bool CreateParallelValidator(Context& context) const {
1047 if (enum_ || context.arrayUniqueness)
1048 context.hasher = context.factory.CreateHasher();
1049
1050 if (validatorCount_) {
1051 RAPIDJSON_ASSERT(context.validators == 0);
1052 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1053 context.validatorCount = validatorCount_;
1054
1055 if (allOf_.schemas)
1056 CreateSchemaValidators(context, allOf_);
1057
1058 if (anyOf_.schemas)
1059 CreateSchemaValidators(context, anyOf_);
1060
1061 if (oneOf_.schemas)
1062 CreateSchemaValidators(context, oneOf_);
1063
1064 if (not_)
1065 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1066
1067 if (hasSchemaDependencies_) {
1068 for (SizeType i = 0; i < propertyCount_; i++)
1069 if (properties_[i].dependenciesSchema)
1070 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1071 }
1072 }
1073
1074 return true;
1075 }
1076
CreateSchemaValidators(Context & context,const SchemaArray & schemas)1077 void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1078 for (SizeType i = 0; i < schemas.count; i++)
1079 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1080 }
1081
1082 // O(n)
FindPropertyIndex(const ValueType & name,SizeType * outIndex)1083 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1084 SizeType len = name.GetStringLength();
1085 const Ch* str = name.GetString();
1086 for (SizeType index = 0; index < propertyCount_; index++)
1087 if (properties_[index].name.GetStringLength() == len &&
1088 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1089 {
1090 *outIndex = index;
1091 return true;
1092 }
1093 return false;
1094 }
1095
CheckInt(Context & context,int64_t i)1096 bool CheckInt(Context& context, int64_t i) const {
1097 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1098 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1099
1100 if (!minimum_.IsNull()) {
1101 if (minimum_.IsInt64()) {
1102 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
1103 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1104 }
1105 else if (minimum_.IsUint64()) {
1106 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1107 }
1108 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1109 return false;
1110 }
1111
1112 if (!maximum_.IsNull()) {
1113 if (maximum_.IsInt64()) {
1114 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
1115 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1116 }
1117 else if (maximum_.IsUint64()) { }
1118 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1119 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1120 return false;
1121 }
1122
1123 if (!multipleOf_.IsNull()) {
1124 if (multipleOf_.IsUint64()) {
1125 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
1126 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1127 }
1128 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1129 return false;
1130 }
1131
1132 return true;
1133 }
1134
CheckUint(Context & context,uint64_t i)1135 bool CheckUint(Context& context, uint64_t i) const {
1136 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1137 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1138
1139 if (!minimum_.IsNull()) {
1140 if (minimum_.IsUint64()) {
1141 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
1142 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1143 }
1144 else if (minimum_.IsInt64())
1145 /* do nothing */; // i >= 0 > minimum.Getint64()
1146 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1147 return false;
1148 }
1149
1150 if (!maximum_.IsNull()) {
1151 if (maximum_.IsUint64()) {
1152 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
1153 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1154 }
1155 else if (maximum_.IsInt64())
1156 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1157 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1158 return false;
1159 }
1160
1161 if (!multipleOf_.IsNull()) {
1162 if (multipleOf_.IsUint64()) {
1163 if (i % multipleOf_.GetUint64() != 0)
1164 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1165 }
1166 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1167 return false;
1168 }
1169
1170 return true;
1171 }
1172
CheckDoubleMinimum(Context & context,double d)1173 bool CheckDoubleMinimum(Context& context, double d) const {
1174 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
1175 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1176 return true;
1177 }
1178
CheckDoubleMaximum(Context & context,double d)1179 bool CheckDoubleMaximum(Context& context, double d) const {
1180 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
1181 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1182 return true;
1183 }
1184
CheckDoubleMultipleOf(Context & context,double d)1185 bool CheckDoubleMultipleOf(Context& context, double d) const {
1186 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1187 double q = std::floor(a / b);
1188 double r = a - q * b;
1189 if (r > 0.0)
1190 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1191 return true;
1192 }
1193
1194 struct Property {
PropertyProperty1195 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
~PropertyProperty1196 ~Property() { AllocatorType::Free(dependencies); }
1197 SValue name;
1198 const SchemaType* schema;
1199 const SchemaType* dependenciesSchema;
1200 SizeType dependenciesValidatorIndex;
1201 bool* dependencies;
1202 bool required;
1203 };
1204
1205 struct PatternProperty {
PatternPropertyPatternProperty1206 PatternProperty() : schema(), pattern() {}
~PatternPropertyPatternProperty1207 ~PatternProperty() {
1208 if (pattern) {
1209 pattern->~RegexType();
1210 AllocatorType::Free(pattern);
1211 }
1212 }
1213 const SchemaType* schema;
1214 RegexType* pattern;
1215 };
1216
1217 AllocatorType* allocator_;
1218 const SchemaType* typeless_;
1219 uint64_t* enum_;
1220 SizeType enumCount_;
1221 SchemaArray allOf_;
1222 SchemaArray anyOf_;
1223 SchemaArray oneOf_;
1224 const SchemaType* not_;
1225 unsigned type_; // bitmask of kSchemaType
1226 SizeType validatorCount_;
1227 SizeType notValidatorIndex_;
1228
1229 Property* properties_;
1230 const SchemaType* additionalPropertiesSchema_;
1231 PatternProperty* patternProperties_;
1232 SizeType patternPropertyCount_;
1233 SizeType propertyCount_;
1234 SizeType minProperties_;
1235 SizeType maxProperties_;
1236 bool additionalProperties_;
1237 bool hasDependencies_;
1238 bool hasRequired_;
1239 bool hasSchemaDependencies_;
1240
1241 const SchemaType* additionalItemsSchema_;
1242 const SchemaType* itemsList_;
1243 const SchemaType** itemsTuple_;
1244 SizeType itemsTupleCount_;
1245 SizeType minItems_;
1246 SizeType maxItems_;
1247 bool additionalItems_;
1248 bool uniqueItems_;
1249
1250 RegexType* pattern_;
1251 SizeType minLength_;
1252 SizeType maxLength_;
1253
1254 SValue minimum_;
1255 SValue maximum_;
1256 SValue multipleOf_;
1257 bool exclusiveMinimum_;
1258 bool exclusiveMaximum_;
1259 };
1260
1261 template<typename Stack, typename Ch>
1262 struct TokenHelper {
AppendIndexTokenTokenHelper1263 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1264 *documentStack.template Push<Ch>() = '/';
1265 char buffer[21];
1266 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1267 for (size_t i = 0; i < length; i++)
1268 *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1269 }
1270 };
1271
1272 // Partial specialized version for char to prevent buffer copying.
1273 template <typename Stack>
1274 struct TokenHelper<Stack, char> {
1275 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1276 if (sizeof(SizeType) == 4) {
1277 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1278 *buffer++ = '/';
1279 const char* end = internal::u32toa(index, buffer);
1280 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1281 }
1282 else {
1283 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1284 *buffer++ = '/';
1285 const char* end = internal::u64toa(index, buffer);
1286 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1287 }
1288 }
1289 };
1290
1291 } // namespace internal
1292
1293 ///////////////////////////////////////////////////////////////////////////////
1294 // IGenericRemoteSchemaDocumentProvider
1295
1296 template <typename SchemaDocumentType>
1297 class IGenericRemoteSchemaDocumentProvider {
1298 public:
1299 typedef typename SchemaDocumentType::Ch Ch;
1300
1301 virtual ~IGenericRemoteSchemaDocumentProvider() {}
1302 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1303 };
1304
1305 ///////////////////////////////////////////////////////////////////////////////
1306 // GenericSchemaDocument
1307
1308 //! JSON schema document.
1309 /*!
1310 A JSON schema document is a compiled version of a JSON schema.
1311 It is basically a tree of internal::Schema.
1312
1313 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1314 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1315 \tparam Allocator Allocator type for allocating memory of this document.
1316 */
1317 template <typename ValueT, typename Allocator = CrtAllocator>
1318 class GenericSchemaDocument {
1319 public:
1320 typedef ValueT ValueType;
1321 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1322 typedef Allocator AllocatorType;
1323 typedef typename ValueType::EncodingType EncodingType;
1324 typedef typename EncodingType::Ch Ch;
1325 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1326 typedef GenericPointer<ValueType, Allocator> PointerType;
1327 friend class internal::Schema<GenericSchemaDocument>;
1328 template <typename, typename, typename>
1329 friend class GenericSchemaValidator;
1330
1331 //! Constructor.
1332 /*!
1333 Compile a JSON document into schema document.
1334
1335 \param document A JSON document as source.
1336 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1337 \param allocator An optional allocator instance for allocating memory. Can be null.
1338 */
1339 explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1340 remoteProvider_(remoteProvider),
1341 allocator_(allocator),
1342 ownAllocator_(),
1343 root_(),
1344 typeless_(),
1345 schemaMap_(allocator, kInitialSchemaMapSize),
1346 schemaRef_(allocator, kInitialSchemaRefSize)
1347 {
1348 if (!allocator_)
1349 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1350
1351 typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1352 new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
1353
1354 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1355 // And call AddRefSchema() if there are $ref.
1356 CreateSchemaRecursive(&root_, PointerType(), document, document);
1357
1358 // Resolve $ref
1359 while (!schemaRef_.Empty()) {
1360 SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1361 if (const SchemaType* s = GetSchema(refEntry->target)) {
1362 if (refEntry->schema)
1363 *refEntry->schema = s;
1364
1365 // Create entry in map if not exist
1366 if (!GetSchema(refEntry->source)) {
1367 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1368 }
1369 }
1370 else if (refEntry->schema)
1371 *refEntry->schema = typeless_;
1372
1373 refEntry->~SchemaRefEntry();
1374 }
1375
1376 RAPIDJSON_ASSERT(root_ != 0);
1377
1378 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1379 }
1380
1381 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1382 //! Move constructor in C++11
1383 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1384 remoteProvider_(rhs.remoteProvider_),
1385 allocator_(rhs.allocator_),
1386 ownAllocator_(rhs.ownAllocator_),
1387 root_(rhs.root_),
1388 typeless_(rhs.typeless_),
1389 schemaMap_(std::move(rhs.schemaMap_)),
1390 schemaRef_(std::move(rhs.schemaRef_))
1391 {
1392 rhs.remoteProvider_ = 0;
1393 rhs.allocator_ = 0;
1394 rhs.ownAllocator_ = 0;
1395 rhs.typeless_ = 0;
1396 }
1397 #endif
1398
1399 //! Destructor
1400 ~GenericSchemaDocument() {
1401 while (!schemaMap_.Empty())
1402 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1403
1404 if (typeless_) {
1405 typeless_->~SchemaType();
1406 Allocator::Free(typeless_);
1407 }
1408
1409 RAPIDJSON_DELETE(ownAllocator_);
1410 }
1411
1412 //! Get the root schema.
1413 const SchemaType& GetRoot() const { return *root_; }
1414
1415 private:
1416 //! Prohibit copying
1417 GenericSchemaDocument(const GenericSchemaDocument&);
1418 //! Prohibit assignment
1419 GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1420
1421 struct SchemaRefEntry {
1422 SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1423 PointerType source;
1424 PointerType target;
1425 const SchemaType** schema;
1426 };
1427
1428 struct SchemaEntry {
1429 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1430 ~SchemaEntry() {
1431 if (owned) {
1432 schema->~SchemaType();
1433 Allocator::Free(schema);
1434 }
1435 }
1436 PointerType pointer;
1437 SchemaType* schema;
1438 bool owned;
1439 };
1440
1441 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1442 if (schema)
1443 *schema = typeless_;
1444
1445 if (v.GetType() == kObjectType) {
1446 const SchemaType* s = GetSchema(pointer);
1447 if (!s)
1448 CreateSchema(schema, pointer, v, document);
1449
1450 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1451 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1452 }
1453 else if (v.GetType() == kArrayType)
1454 for (SizeType i = 0; i < v.Size(); i++)
1455 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1456 }
1457
1458 void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1459 RAPIDJSON_ASSERT(pointer.IsValid());
1460 if (v.IsObject()) {
1461 if (!HandleRefSchema(pointer, schema, v, document)) {
1462 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1463 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1464 if (schema)
1465 *schema = s;
1466 }
1467 }
1468 }
1469
1470 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1471 static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1472 static const ValueType kRefValue(kRefString, 4);
1473
1474 typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1475 if (itr == v.MemberEnd())
1476 return false;
1477
1478 if (itr->value.IsString()) {
1479 SizeType len = itr->value.GetStringLength();
1480 if (len > 0) {
1481 const Ch* s = itr->value.GetString();
1482 SizeType i = 0;
1483 while (i < len && s[i] != '#') // Find the first #
1484 i++;
1485
1486 if (i > 0) { // Remote reference, resolve immediately
1487 if (remoteProvider_) {
1488 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
1489 PointerType pointer(&s[i], len - i, allocator_);
1490 if (pointer.IsValid()) {
1491 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1492 if (schema)
1493 *schema = sc;
1494 return true;
1495 }
1496 }
1497 }
1498 }
1499 }
1500 else if (s[i] == '#') { // Local reference, defer resolution
1501 PointerType pointer(&s[i], len - i, allocator_);
1502 if (pointer.IsValid()) {
1503 if (const ValueType* nv = pointer.Get(document))
1504 if (HandleRefSchema(source, schema, *nv, document))
1505 return true;
1506
1507 new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1508 return true;
1509 }
1510 }
1511 }
1512 }
1513 return false;
1514 }
1515
1516 const SchemaType* GetSchema(const PointerType& pointer) const {
1517 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1518 if (pointer == target->pointer)
1519 return target->schema;
1520 return 0;
1521 }
1522
1523 PointerType GetPointer(const SchemaType* schema) const {
1524 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1525 if (schema == target->schema)
1526 return target->pointer;
1527 return PointerType();
1528 }
1529
1530 const SchemaType* GetTypeless() const { return typeless_; }
1531
1532 static const size_t kInitialSchemaMapSize = 64;
1533 static const size_t kInitialSchemaRefSize = 64;
1534
1535 IRemoteSchemaDocumentProviderType* remoteProvider_;
1536 Allocator *allocator_;
1537 Allocator *ownAllocator_;
1538 const SchemaType* root_; //!< Root schema.
1539 SchemaType* typeless_;
1540 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1541 internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1542 };
1543
1544 //! GenericSchemaDocument using Value type.
1545 typedef GenericSchemaDocument<Value> SchemaDocument;
1546 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1547 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1548
1549 ///////////////////////////////////////////////////////////////////////////////
1550 // GenericSchemaValidator
1551
1552 //! JSON Schema Validator.
1553 /*!
1554 A SAX style JSON schema validator.
1555 It uses a \c GenericSchemaDocument to validate SAX events.
1556 It delegates the incoming SAX events to an output handler.
1557 The default output handler does nothing.
1558 It can be reused multiple times by calling \c Reset().
1559
1560 \tparam SchemaDocumentType Type of schema document.
1561 \tparam OutputHandler Type of output handler. Default handler does nothing.
1562 \tparam StateAllocator Allocator for storing the internal validation states.
1563 */
1564 template <
1565 typename SchemaDocumentType,
1566 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1567 typename StateAllocator = CrtAllocator>
1568 class GenericSchemaValidator :
1569 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1570 public internal::ISchemaValidator
1571 {
1572 public:
1573 typedef typename SchemaDocumentType::SchemaType SchemaType;
1574 typedef typename SchemaDocumentType::PointerType PointerType;
1575 typedef typename SchemaType::EncodingType EncodingType;
1576 typedef typename EncodingType::Ch Ch;
1577
1578 //! Constructor without output handler.
1579 /*!
1580 \param schemaDocument The schema document to conform to.
1581 \param allocator Optional allocator for storing internal validation states.
1582 \param schemaStackCapacity Optional initial capacity of schema path stack.
1583 \param documentStackCapacity Optional initial capacity of document path stack.
1584 */
1585 GenericSchemaValidator(
1586 const SchemaDocumentType& schemaDocument,
1587 StateAllocator* allocator = 0,
1588 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1589 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1590 :
1591 schemaDocument_(&schemaDocument),
1592 root_(schemaDocument.GetRoot()),
1593 stateAllocator_(allocator),
1594 ownStateAllocator_(0),
1595 schemaStack_(allocator, schemaStackCapacity),
1596 documentStack_(allocator, documentStackCapacity),
1597 outputHandler_(0),
1598 valid_(true)
1599 #if RAPIDJSON_SCHEMA_VERBOSE
1600 , depth_(0)
1601 #endif
1602 {
1603 }
1604
1605 //! Constructor with output handler.
1606 /*!
1607 \param schemaDocument The schema document to conform to.
1608 \param allocator Optional allocator for storing internal validation states.
1609 \param schemaStackCapacity Optional initial capacity of schema path stack.
1610 \param documentStackCapacity Optional initial capacity of document path stack.
1611 */
1612 GenericSchemaValidator(
1613 const SchemaDocumentType& schemaDocument,
1614 OutputHandler& outputHandler,
1615 StateAllocator* allocator = 0,
1616 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1617 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1618 :
1619 schemaDocument_(&schemaDocument),
1620 root_(schemaDocument.GetRoot()),
1621 stateAllocator_(allocator),
1622 ownStateAllocator_(0),
1623 schemaStack_(allocator, schemaStackCapacity),
1624 documentStack_(allocator, documentStackCapacity),
1625 outputHandler_(&outputHandler),
1626 valid_(true)
1627 #if RAPIDJSON_SCHEMA_VERBOSE
1628 , depth_(0)
1629 #endif
1630 {
1631 }
1632
1633 //! Destructor.
1634 ~GenericSchemaValidator() {
1635 Reset();
1636 RAPIDJSON_DELETE(ownStateAllocator_);
1637 }
1638
1639 //! Reset the internal states.
1640 void Reset() {
1641 while (!schemaStack_.Empty())
1642 PopSchema();
1643 documentStack_.Clear();
1644 valid_ = true;
1645 }
1646
1647 //! Checks whether the current state is valid.
1648 // Implementation of ISchemaValidator
1649 virtual bool IsValid() const { return valid_; }
1650
1651 //! Gets the JSON pointer pointed to the invalid schema.
1652 PointerType GetInvalidSchemaPointer() const {
1653 return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
1654 }
1655
1656 //! Gets the keyword of invalid schema.
1657 const Ch* GetInvalidSchemaKeyword() const {
1658 return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1659 }
1660
1661 //! Gets the JSON pointer pointed to the invalid value.
1662 PointerType GetInvalidDocumentPointer() const {
1663 return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1664 }
1665
1666 #if RAPIDJSON_SCHEMA_VERBOSE
1667 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
1668 RAPIDJSON_MULTILINEMACRO_BEGIN\
1669 *documentStack_.template Push<Ch>() = '\0';\
1670 documentStack_.template Pop<Ch>(1);\
1671 internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
1672 RAPIDJSON_MULTILINEMACRO_END
1673 #else
1674 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
1675 #endif
1676
1677 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
1678 if (!valid_) return false; \
1679 if (!BeginValue() || !CurrentSchema().method arg1) {\
1680 RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
1681 return valid_ = false;\
1682 }
1683
1684 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
1685 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
1686 if (context->hasher)\
1687 static_cast<HasherType*>(context->hasher)->method arg2;\
1688 if (context->validators)\
1689 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
1690 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
1691 if (context->patternPropertiesValidators)\
1692 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
1693 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
1694 }
1695
1696 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
1697 return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
1698
1699 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
1700 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
1701 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
1702 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
1703
1704 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
1705 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
1706 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
1707 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
1708 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
1709 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
1710 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
1711 bool RawNumber(const Ch* str, SizeType length, bool copy)
1712 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1713 bool String(const Ch* str, SizeType length, bool copy)
1714 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1715
1716 bool StartObject() {
1717 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
1718 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
1719 return valid_ = !outputHandler_ || outputHandler_->StartObject();
1720 }
1721
1722 bool Key(const Ch* str, SizeType len, bool copy) {
1723 if (!valid_) return false;
1724 AppendToken(str, len);
1725 if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
1726 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
1727 return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
1728 }
1729
1730 bool EndObject(SizeType memberCount) {
1731 if (!valid_) return false;
1732 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
1733 if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
1734 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
1735 }
1736
1737 bool StartArray() {
1738 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
1739 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
1740 return valid_ = !outputHandler_ || outputHandler_->StartArray();
1741 }
1742
1743 bool EndArray(SizeType elementCount) {
1744 if (!valid_) return false;
1745 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
1746 if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
1747 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
1748 }
1749
1750 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
1751 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
1752 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
1753 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
1754
1755 // Implementation of ISchemaStateFactory<SchemaType>
1756 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
1757 return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
1758 #if RAPIDJSON_SCHEMA_VERBOSE
1759 depth_ + 1,
1760 #endif
1761 &GetStateAllocator());
1762 }
1763
1764 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
1765 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
1766 v->~GenericSchemaValidator();
1767 StateAllocator::Free(v);
1768 }
1769
1770 virtual void* CreateHasher() {
1771 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
1772 }
1773
1774 virtual uint64_t GetHashCode(void* hasher) {
1775 return static_cast<HasherType*>(hasher)->GetHashCode();
1776 }
1777
1778 virtual void DestroryHasher(void* hasher) {
1779 HasherType* h = static_cast<HasherType*>(hasher);
1780 h->~HasherType();
1781 StateAllocator::Free(h);
1782 }
1783
1784 virtual void* MallocState(size_t size) {
1785 return GetStateAllocator().Malloc(size);
1786 }
1787
1788 virtual void FreeState(void* p) {
1789 StateAllocator::Free(p);
1790 }
1791
1792 private:
1793 typedef typename SchemaType::Context Context;
1794 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
1795 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
1796
1797 GenericSchemaValidator(
1798 const SchemaDocumentType& schemaDocument,
1799 const SchemaType& root,
1800 #if RAPIDJSON_SCHEMA_VERBOSE
1801 unsigned depth,
1802 #endif
1803 StateAllocator* allocator = 0,
1804 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1805 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1806 :
1807 schemaDocument_(&schemaDocument),
1808 root_(root),
1809 stateAllocator_(allocator),
1810 ownStateAllocator_(0),
1811 schemaStack_(allocator, schemaStackCapacity),
1812 documentStack_(allocator, documentStackCapacity),
1813 outputHandler_(0),
1814 valid_(true)
1815 #if RAPIDJSON_SCHEMA_VERBOSE
1816 , depth_(depth)
1817 #endif
1818 {
1819 }
1820
1821 StateAllocator& GetStateAllocator() {
1822 if (!stateAllocator_)
1823 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
1824 return *stateAllocator_;
1825 }
1826
1827 bool BeginValue() {
1828 if (schemaStack_.Empty())
1829 PushSchema(root_);
1830 else {
1831 if (CurrentContext().inArray)
1832 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
1833
1834 if (!CurrentSchema().BeginValue(CurrentContext()))
1835 return false;
1836
1837 SizeType count = CurrentContext().patternPropertiesSchemaCount;
1838 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
1839 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
1840 bool valueUniqueness = CurrentContext().valueUniqueness;
1841 RAPIDJSON_ASSERT(CurrentContext().valueSchema);
1842 PushSchema(*CurrentContext().valueSchema);
1843
1844 if (count > 0) {
1845 CurrentContext().objectPatternValidatorType = patternValidatorType;
1846 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
1847 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
1848 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
1849 for (SizeType i = 0; i < count; i++)
1850 va[validatorCount++] = CreateSchemaValidator(*sa[i]);
1851 }
1852
1853 CurrentContext().arrayUniqueness = valueUniqueness;
1854 }
1855 return true;
1856 }
1857
1858 bool EndValue() {
1859 if (!CurrentSchema().EndValue(CurrentContext()))
1860 return false;
1861
1862 #if RAPIDJSON_SCHEMA_VERBOSE
1863 GenericStringBuffer<EncodingType> sb;
1864 schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
1865
1866 *documentStack_.template Push<Ch>() = '\0';
1867 documentStack_.template Pop<Ch>(1);
1868 internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
1869 #endif
1870
1871 uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
1872
1873 PopSchema();
1874
1875 if (!schemaStack_.Empty()) {
1876 Context& context = CurrentContext();
1877 if (context.valueUniqueness) {
1878 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
1879 if (!a)
1880 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
1881 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
1882 if (itr->GetUint64() == h)
1883 RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
1884 a->PushBack(h, GetStateAllocator());
1885 }
1886 }
1887
1888 // Remove the last token of document pointer
1889 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
1890 ;
1891
1892 return true;
1893 }
1894
1895 void AppendToken(const Ch* str, SizeType len) {
1896 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
1897 *documentStack_.template PushUnsafe<Ch>() = '/';
1898 for (SizeType i = 0; i < len; i++) {
1899 if (str[i] == '~') {
1900 *documentStack_.template PushUnsafe<Ch>() = '~';
1901 *documentStack_.template PushUnsafe<Ch>() = '0';
1902 }
1903 else if (str[i] == '/') {
1904 *documentStack_.template PushUnsafe<Ch>() = '~';
1905 *documentStack_.template PushUnsafe<Ch>() = '1';
1906 }
1907 else
1908 *documentStack_.template PushUnsafe<Ch>() = str[i];
1909 }
1910 }
1911
1912 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
1913
1914 RAPIDJSON_FORCEINLINE void PopSchema() {
1915 Context* c = schemaStack_.template Pop<Context>(1);
1916 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
1917 a->~HashCodeArray();
1918 StateAllocator::Free(a);
1919 }
1920 c->~Context();
1921 }
1922
1923 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
1924 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
1925 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
1926
1927 static const size_t kDefaultSchemaStackCapacity = 1024;
1928 static const size_t kDefaultDocumentStackCapacity = 256;
1929 const SchemaDocumentType* schemaDocument_;
1930 const SchemaType& root_;
1931 StateAllocator* stateAllocator_;
1932 StateAllocator* ownStateAllocator_;
1933 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
1934 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
1935 OutputHandler* outputHandler_;
1936 bool valid_;
1937 #if RAPIDJSON_SCHEMA_VERBOSE
1938 unsigned depth_;
1939 #endif
1940 };
1941
1942 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
1943
1944 ///////////////////////////////////////////////////////////////////////////////
1945 // SchemaValidatingReader
1946
1947 //! A helper class for parsing with validation.
1948 /*!
1949 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
1950
1951 \tparam parseFlags Combination of \ref ParseFlag.
1952 \tparam InputStream Type of input stream, implementing Stream concept.
1953 \tparam SourceEncoding Encoding of the input stream.
1954 \tparam SchemaDocumentType Type of schema document.
1955 \tparam StackAllocator Allocator type for stack.
1956 */
1957 template <
1958 unsigned parseFlags,
1959 typename InputStream,
1960 typename SourceEncoding,
1961 typename SchemaDocumentType = SchemaDocument,
1962 typename StackAllocator = CrtAllocator>
1963 class SchemaValidatingReader {
1964 public:
1965 typedef typename SchemaDocumentType::PointerType PointerType;
1966 typedef typename InputStream::Ch Ch;
1967
1968 //! Constructor
1969 /*!
1970 \param is Input stream.
1971 \param sd Schema document.
1972 */
1973 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
1974
1975 template <typename Handler>
1976 bool operator()(Handler& handler) {
1977 GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
1978 GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
1979 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
1980
1981 isValid_ = validator.IsValid();
1982 if (isValid_) {
1983 invalidSchemaPointer_ = PointerType();
1984 invalidSchemaKeyword_ = 0;
1985 invalidDocumentPointer_ = PointerType();
1986 }
1987 else {
1988 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
1989 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
1990 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
1991 }
1992
1993 return parseResult_;
1994 }
1995
1996 const ParseResult& GetParseResult() const { return parseResult_; }
1997 bool IsValid() const { return isValid_; }
1998 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
1999 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
2000 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
2001
2002 private:
2003 InputStream& is_;
2004 const SchemaDocumentType& sd_;
2005
2006 ParseResult parseResult_;
2007 PointerType invalidSchemaPointer_;
2008 const Ch* invalidSchemaKeyword_;
2009 PointerType invalidDocumentPointer_;
2010 bool isValid_;
2011 };
2012
2013 RAPIDJSON_NAMESPACE_END
2014 RAPIDJSON_DIAG_POP
2015
2016 #endif // RAPIDJSON_SCHEMA_H_
2017