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 }
789
790 SizeType index;
791 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
792 if (context.patternPropertiesSchemaCount > 0) {
793 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
794 context.valueSchema = typeless_;
795 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
796 }
797 else
798 context.valueSchema = properties_[index].schema;
799
800 if (context.propertyExist)
801 context.propertyExist[index] = true;
802
803 return true;
804 }
805
806 if (additionalPropertiesSchema_) {
807 if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
808 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
809 context.valueSchema = typeless_;
810 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
811 }
812 else
813 context.valueSchema = additionalPropertiesSchema_;
814 return true;
815 }
816 else if (additionalProperties_) {
817 context.valueSchema = typeless_;
818 return true;
819 }
820
821 if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
822 RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
823
824 return true;
825 }
826
EndObject(Context & context,SizeType memberCount)827 bool EndObject(Context& context, SizeType memberCount) const {
828 if (hasRequired_)
829 for (SizeType index = 0; index < propertyCount_; index++)
830 if (properties_[index].required)
831 if (!context.propertyExist[index])
832 RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
833
834 if (memberCount < minProperties_)
835 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
836
837 if (memberCount > maxProperties_)
838 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
839
840 if (hasDependencies_) {
841 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
842 if (context.propertyExist[sourceIndex]) {
843 if (properties_[sourceIndex].dependencies) {
844 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
845 if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
846 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
847 }
848 else if (properties_[sourceIndex].dependenciesSchema)
849 if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
850 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
851 }
852 }
853
854 return true;
855 }
856
StartArray(Context & context)857 bool StartArray(Context& context) const {
858 if (!(type_ & (1 << kArraySchemaType)))
859 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
860
861 context.arrayElementIndex = 0;
862 context.inArray = true;
863
864 return CreateParallelValidator(context);
865 }
866
EndArray(Context & context,SizeType elementCount)867 bool EndArray(Context& context, SizeType elementCount) const {
868 context.inArray = false;
869
870 if (elementCount < minItems_)
871 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
872
873 if (elementCount > maxItems_)
874 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
875
876 return true;
877 }
878
879 // Generate functions for string literal according to Ch
880 #define RAPIDJSON_STRING_(name, ...) \
881 static const ValueType& Get##name##String() {\
882 static const Ch s[] = { __VA_ARGS__, '\0' };\
883 static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
884 return v;\
885 }
886
887 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
888 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
889 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
890 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
891 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
892 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
893 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
894 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
895 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
896 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
897 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
898 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
899 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
900 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
901 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
902 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
903 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
904 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
905 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
906 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
907 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
908 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
909 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
910 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
911 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
912 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
913 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
914 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
915 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
916 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
917 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
918 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
919 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
920
921 #undef RAPIDJSON_STRING_
922
923 private:
924 enum SchemaValueType {
925 kNullSchemaType,
926 kBooleanSchemaType,
927 kObjectSchemaType,
928 kArraySchemaType,
929 kStringSchemaType,
930 kNumberSchemaType,
931 kIntegerSchemaType,
932 kTotalSchemaType
933 };
934
935 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
936 typedef internal::GenericRegex<EncodingType> RegexType;
937 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
938 typedef std::basic_regex<Ch> RegexType;
939 #else
940 typedef char RegexType;
941 #endif
942
943 struct SchemaArray {
SchemaArraySchemaArray944 SchemaArray() : schemas(), count() {}
~SchemaArraySchemaArray945 ~SchemaArray() { AllocatorType::Free(schemas); }
946 const SchemaType** schemas;
947 SizeType begin; // begin index of context.validators
948 SizeType count;
949 };
950
951 template <typename V1, typename V2>
AddUniqueElement(V1 & a,const V2 & v)952 void AddUniqueElement(V1& a, const V2& v) {
953 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
954 if (*itr == v)
955 return;
956 V1 c(v, *allocator_);
957 a.PushBack(c, *allocator_);
958 }
959
GetMember(const ValueType & value,const ValueType & name)960 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
961 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
962 return itr != value.MemberEnd() ? &(itr->value) : 0;
963 }
964
AssignIfExist(bool & out,const ValueType & value,const ValueType & name)965 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
966 if (const ValueType* v = GetMember(value, name))
967 if (v->IsBool())
968 out = v->GetBool();
969 }
970
AssignIfExist(SizeType & out,const ValueType & value,const ValueType & name)971 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
972 if (const ValueType* v = GetMember(value, name))
973 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
974 out = static_cast<SizeType>(v->GetUint64());
975 }
976
AssignIfExist(SchemaArray & out,SchemaDocumentType & schemaDocument,const PointerType & p,const ValueType & value,const ValueType & name,const ValueType & document)977 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
978 if (const ValueType* v = GetMember(value, name)) {
979 if (v->IsArray() && v->Size() > 0) {
980 PointerType q = p.Append(name, allocator_);
981 out.count = v->Size();
982 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
983 memset(out.schemas, 0, sizeof(Schema*)* out.count);
984 for (SizeType i = 0; i < out.count; i++)
985 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
986 out.begin = validatorCount_;
987 validatorCount_ += out.count;
988 }
989 }
990 }
991
992 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
993 template <typename ValueType>
CreatePattern(const ValueType & value)994 RegexType* CreatePattern(const ValueType& value) {
995 if (value.IsString()) {
996 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
997 if (!r->IsValid()) {
998 r->~RegexType();
999 AllocatorType::Free(r);
1000 r = 0;
1001 }
1002 return r;
1003 }
1004 return 0;
1005 }
1006
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType)1007 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1008 GenericRegexSearch<RegexType> rs(*pattern);
1009 return rs.Search(str);
1010 }
1011 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1012 template <typename ValueType>
CreatePattern(const ValueType & value)1013 RegexType* CreatePattern(const ValueType& value) {
1014 if (value.IsString())
1015 try {
1016 return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1017 }
1018 catch (const std::regex_error&) {
1019 }
1020 return 0;
1021 }
1022
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType length)1023 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1024 std::match_results<const Ch*> r;
1025 return std::regex_search(str, str + length, r, *pattern);
1026 }
1027 #else
1028 template <typename ValueType>
CreatePattern(const ValueType &)1029 RegexType* CreatePattern(const ValueType&) { return 0; }
1030
IsPatternMatch(const RegexType *,const Ch *,SizeType)1031 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1032 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1033
AddType(const ValueType & type)1034 void AddType(const ValueType& type) {
1035 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1036 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1037 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1038 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1039 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1040 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1041 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1042 }
1043
CreateParallelValidator(Context & context)1044 bool CreateParallelValidator(Context& context) const {
1045 if (enum_ || context.arrayUniqueness)
1046 context.hasher = context.factory.CreateHasher();
1047
1048 if (validatorCount_) {
1049 RAPIDJSON_ASSERT(context.validators == 0);
1050 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1051 context.validatorCount = validatorCount_;
1052
1053 if (allOf_.schemas)
1054 CreateSchemaValidators(context, allOf_);
1055
1056 if (anyOf_.schemas)
1057 CreateSchemaValidators(context, anyOf_);
1058
1059 if (oneOf_.schemas)
1060 CreateSchemaValidators(context, oneOf_);
1061
1062 if (not_)
1063 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1064
1065 if (hasSchemaDependencies_) {
1066 for (SizeType i = 0; i < propertyCount_; i++)
1067 if (properties_[i].dependenciesSchema)
1068 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1069 }
1070 }
1071
1072 return true;
1073 }
1074
CreateSchemaValidators(Context & context,const SchemaArray & schemas)1075 void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1076 for (SizeType i = 0; i < schemas.count; i++)
1077 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1078 }
1079
1080 // O(n)
FindPropertyIndex(const ValueType & name,SizeType * outIndex)1081 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1082 SizeType len = name.GetStringLength();
1083 const Ch* str = name.GetString();
1084 for (SizeType index = 0; index < propertyCount_; index++)
1085 if (properties_[index].name.GetStringLength() == len &&
1086 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1087 {
1088 *outIndex = index;
1089 return true;
1090 }
1091 return false;
1092 }
1093
CheckInt(Context & context,int64_t i)1094 bool CheckInt(Context& context, int64_t i) const {
1095 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1096 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1097
1098 if (!minimum_.IsNull()) {
1099 if (minimum_.IsInt64()) {
1100 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
1101 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1102 }
1103 else if (minimum_.IsUint64()) {
1104 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1105 }
1106 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1107 return false;
1108 }
1109
1110 if (!maximum_.IsNull()) {
1111 if (maximum_.IsInt64()) {
1112 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
1113 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1114 }
1115 else if (maximum_.IsUint64()) { }
1116 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1117 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1118 return false;
1119 }
1120
1121 if (!multipleOf_.IsNull()) {
1122 if (multipleOf_.IsUint64()) {
1123 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
1124 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1125 }
1126 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1127 return false;
1128 }
1129
1130 return true;
1131 }
1132
CheckUint(Context & context,uint64_t i)1133 bool CheckUint(Context& context, uint64_t i) const {
1134 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1135 RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1136
1137 if (!minimum_.IsNull()) {
1138 if (minimum_.IsUint64()) {
1139 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
1140 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1141 }
1142 else if (minimum_.IsInt64())
1143 /* do nothing */; // i >= 0 > minimum.Getint64()
1144 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1145 return false;
1146 }
1147
1148 if (!maximum_.IsNull()) {
1149 if (maximum_.IsUint64()) {
1150 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
1151 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1152 }
1153 else if (maximum_.IsInt64())
1154 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1155 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1156 return false;
1157 }
1158
1159 if (!multipleOf_.IsNull()) {
1160 if (multipleOf_.IsUint64()) {
1161 if (i % multipleOf_.GetUint64() != 0)
1162 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1163 }
1164 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1165 return false;
1166 }
1167
1168 return true;
1169 }
1170
CheckDoubleMinimum(Context & context,double d)1171 bool CheckDoubleMinimum(Context& context, double d) const {
1172 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
1173 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1174 return true;
1175 }
1176
CheckDoubleMaximum(Context & context,double d)1177 bool CheckDoubleMaximum(Context& context, double d) const {
1178 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
1179 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1180 return true;
1181 }
1182
CheckDoubleMultipleOf(Context & context,double d)1183 bool CheckDoubleMultipleOf(Context& context, double d) const {
1184 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1185 double q = std::floor(a / b);
1186 double r = a - q * b;
1187 if (r > 0.0)
1188 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1189 return true;
1190 }
1191
1192 struct Property {
PropertyProperty1193 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
~PropertyProperty1194 ~Property() { AllocatorType::Free(dependencies); }
1195 SValue name;
1196 const SchemaType* schema;
1197 const SchemaType* dependenciesSchema;
1198 SizeType dependenciesValidatorIndex;
1199 bool* dependencies;
1200 bool required;
1201 };
1202
1203 struct PatternProperty {
PatternPropertyPatternProperty1204 PatternProperty() : schema(), pattern() {}
~PatternPropertyPatternProperty1205 ~PatternProperty() {
1206 if (pattern) {
1207 pattern->~RegexType();
1208 AllocatorType::Free(pattern);
1209 }
1210 }
1211 const SchemaType* schema;
1212 RegexType* pattern;
1213 };
1214
1215 AllocatorType* allocator_;
1216 const SchemaType* typeless_;
1217 uint64_t* enum_;
1218 SizeType enumCount_;
1219 SchemaArray allOf_;
1220 SchemaArray anyOf_;
1221 SchemaArray oneOf_;
1222 const SchemaType* not_;
1223 unsigned type_; // bitmask of kSchemaType
1224 SizeType validatorCount_;
1225 SizeType notValidatorIndex_;
1226
1227 Property* properties_;
1228 const SchemaType* additionalPropertiesSchema_;
1229 PatternProperty* patternProperties_;
1230 SizeType patternPropertyCount_;
1231 SizeType propertyCount_;
1232 SizeType minProperties_;
1233 SizeType maxProperties_;
1234 bool additionalProperties_;
1235 bool hasDependencies_;
1236 bool hasRequired_;
1237 bool hasSchemaDependencies_;
1238
1239 const SchemaType* additionalItemsSchema_;
1240 const SchemaType* itemsList_;
1241 const SchemaType** itemsTuple_;
1242 SizeType itemsTupleCount_;
1243 SizeType minItems_;
1244 SizeType maxItems_;
1245 bool additionalItems_;
1246 bool uniqueItems_;
1247
1248 RegexType* pattern_;
1249 SizeType minLength_;
1250 SizeType maxLength_;
1251
1252 SValue minimum_;
1253 SValue maximum_;
1254 SValue multipleOf_;
1255 bool exclusiveMinimum_;
1256 bool exclusiveMaximum_;
1257 };
1258
1259 template<typename Stack, typename Ch>
1260 struct TokenHelper {
AppendIndexTokenTokenHelper1261 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1262 *documentStack.template Push<Ch>() = '/';
1263 char buffer[21];
1264 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1265 for (size_t i = 0; i < length; i++)
1266 *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1267 }
1268 };
1269
1270 // Partial specialized version for char to prevent buffer copying.
1271 template <typename Stack>
1272 struct TokenHelper<Stack, char> {
1273 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1274 if (sizeof(SizeType) == 4) {
1275 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1276 *buffer++ = '/';
1277 const char* end = internal::u32toa(index, buffer);
1278 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1279 }
1280 else {
1281 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1282 *buffer++ = '/';
1283 const char* end = internal::u64toa(index, buffer);
1284 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1285 }
1286 }
1287 };
1288
1289 } // namespace internal
1290
1291 ///////////////////////////////////////////////////////////////////////////////
1292 // IGenericRemoteSchemaDocumentProvider
1293
1294 template <typename SchemaDocumentType>
1295 class IGenericRemoteSchemaDocumentProvider {
1296 public:
1297 typedef typename SchemaDocumentType::Ch Ch;
1298
1299 virtual ~IGenericRemoteSchemaDocumentProvider() {}
1300 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1301 };
1302
1303 ///////////////////////////////////////////////////////////////////////////////
1304 // GenericSchemaDocument
1305
1306 //! JSON schema document.
1307 /*!
1308 A JSON schema document is a compiled version of a JSON schema.
1309 It is basically a tree of internal::Schema.
1310
1311 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1312 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1313 \tparam Allocator Allocator type for allocating memory of this document.
1314 */
1315 template <typename ValueT, typename Allocator = CrtAllocator>
1316 class GenericSchemaDocument {
1317 public:
1318 typedef ValueT ValueType;
1319 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1320 typedef Allocator AllocatorType;
1321 typedef typename ValueType::EncodingType EncodingType;
1322 typedef typename EncodingType::Ch Ch;
1323 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1324 typedef GenericPointer<ValueType, Allocator> PointerType;
1325 friend class internal::Schema<GenericSchemaDocument>;
1326 template <typename, typename, typename>
1327 friend class GenericSchemaValidator;
1328
1329 //! Constructor.
1330 /*!
1331 Compile a JSON document into schema document.
1332
1333 \param document A JSON document as source.
1334 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1335 \param allocator An optional allocator instance for allocating memory. Can be null.
1336 */
1337 explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1338 remoteProvider_(remoteProvider),
1339 allocator_(allocator),
1340 ownAllocator_(),
1341 root_(),
1342 typeless_(),
1343 schemaMap_(allocator, kInitialSchemaMapSize),
1344 schemaRef_(allocator, kInitialSchemaRefSize)
1345 {
1346 if (!allocator_)
1347 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1348
1349 typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1350 new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
1351
1352 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1353 // And call AddRefSchema() if there are $ref.
1354 CreateSchemaRecursive(&root_, PointerType(), document, document);
1355
1356 // Resolve $ref
1357 while (!schemaRef_.Empty()) {
1358 SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1359 if (const SchemaType* s = GetSchema(refEntry->target)) {
1360 if (refEntry->schema)
1361 *refEntry->schema = s;
1362
1363 // Create entry in map if not exist
1364 if (!GetSchema(refEntry->source)) {
1365 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1366 }
1367 }
1368 else if (refEntry->schema)
1369 *refEntry->schema = typeless_;
1370
1371 refEntry->~SchemaRefEntry();
1372 }
1373
1374 RAPIDJSON_ASSERT(root_ != 0);
1375
1376 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1377 }
1378
1379 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1380 //! Move constructor in C++11
1381 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1382 remoteProvider_(rhs.remoteProvider_),
1383 allocator_(rhs.allocator_),
1384 ownAllocator_(rhs.ownAllocator_),
1385 root_(rhs.root_),
1386 typeless_(rhs.typeless_),
1387 schemaMap_(std::move(rhs.schemaMap_)),
1388 schemaRef_(std::move(rhs.schemaRef_))
1389 {
1390 rhs.remoteProvider_ = 0;
1391 rhs.allocator_ = 0;
1392 rhs.ownAllocator_ = 0;
1393 rhs.typeless_ = 0;
1394 }
1395 #endif
1396
1397 //! Destructor
1398 ~GenericSchemaDocument() {
1399 while (!schemaMap_.Empty())
1400 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1401
1402 if (typeless_) {
1403 typeless_->~SchemaType();
1404 Allocator::Free(typeless_);
1405 }
1406
1407 RAPIDJSON_DELETE(ownAllocator_);
1408 }
1409
1410 //! Get the root schema.
1411 const SchemaType& GetRoot() const { return *root_; }
1412
1413 private:
1414 //! Prohibit copying
1415 GenericSchemaDocument(const GenericSchemaDocument&);
1416 //! Prohibit assignment
1417 GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1418
1419 struct SchemaRefEntry {
1420 SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1421 PointerType source;
1422 PointerType target;
1423 const SchemaType** schema;
1424 };
1425
1426 struct SchemaEntry {
1427 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1428 ~SchemaEntry() {
1429 if (owned) {
1430 schema->~SchemaType();
1431 Allocator::Free(schema);
1432 }
1433 }
1434 PointerType pointer;
1435 SchemaType* schema;
1436 bool owned;
1437 };
1438
1439 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1440 if (schema)
1441 *schema = typeless_;
1442
1443 if (v.GetType() == kObjectType) {
1444 const SchemaType* s = GetSchema(pointer);
1445 if (!s)
1446 CreateSchema(schema, pointer, v, document);
1447
1448 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1449 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1450 }
1451 else if (v.GetType() == kArrayType)
1452 for (SizeType i = 0; i < v.Size(); i++)
1453 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1454 }
1455
1456 void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1457 RAPIDJSON_ASSERT(pointer.IsValid());
1458 if (v.IsObject()) {
1459 if (!HandleRefSchema(pointer, schema, v, document)) {
1460 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1461 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1462 if (schema)
1463 *schema = s;
1464 }
1465 }
1466 }
1467
1468 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1469 static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1470 static const ValueType kRefValue(kRefString, 4);
1471
1472 typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1473 if (itr == v.MemberEnd())
1474 return false;
1475
1476 if (itr->value.IsString()) {
1477 SizeType len = itr->value.GetStringLength();
1478 if (len > 0) {
1479 const Ch* s = itr->value.GetString();
1480 SizeType i = 0;
1481 while (i < len && s[i] != '#') // Find the first #
1482 i++;
1483
1484 if (i > 0) { // Remote reference, resolve immediately
1485 if (remoteProvider_) {
1486 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
1487 PointerType pointer(&s[i], len - i, allocator_);
1488 if (pointer.IsValid()) {
1489 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1490 if (schema)
1491 *schema = sc;
1492 return true;
1493 }
1494 }
1495 }
1496 }
1497 }
1498 else if (s[i] == '#') { // Local reference, defer resolution
1499 PointerType pointer(&s[i], len - i, allocator_);
1500 if (pointer.IsValid()) {
1501 if (const ValueType* nv = pointer.Get(document))
1502 if (HandleRefSchema(source, schema, *nv, document))
1503 return true;
1504
1505 new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1506 return true;
1507 }
1508 }
1509 }
1510 }
1511 return false;
1512 }
1513
1514 const SchemaType* GetSchema(const PointerType& pointer) const {
1515 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1516 if (pointer == target->pointer)
1517 return target->schema;
1518 return 0;
1519 }
1520
1521 PointerType GetPointer(const SchemaType* schema) const {
1522 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1523 if (schema == target->schema)
1524 return target->pointer;
1525 return PointerType();
1526 }
1527
1528 const SchemaType* GetTypeless() const { return typeless_; }
1529
1530 static const size_t kInitialSchemaMapSize = 64;
1531 static const size_t kInitialSchemaRefSize = 64;
1532
1533 IRemoteSchemaDocumentProviderType* remoteProvider_;
1534 Allocator *allocator_;
1535 Allocator *ownAllocator_;
1536 const SchemaType* root_; //!< Root schema.
1537 SchemaType* typeless_;
1538 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1539 internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1540 };
1541
1542 //! GenericSchemaDocument using Value type.
1543 typedef GenericSchemaDocument<Value> SchemaDocument;
1544 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1545 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1546
1547 ///////////////////////////////////////////////////////////////////////////////
1548 // GenericSchemaValidator
1549
1550 //! JSON Schema Validator.
1551 /*!
1552 A SAX style JSON schema validator.
1553 It uses a \c GenericSchemaDocument to validate SAX events.
1554 It delegates the incoming SAX events to an output handler.
1555 The default output handler does nothing.
1556 It can be reused multiple times by calling \c Reset().
1557
1558 \tparam SchemaDocumentType Type of schema document.
1559 \tparam OutputHandler Type of output handler. Default handler does nothing.
1560 \tparam StateAllocator Allocator for storing the internal validation states.
1561 */
1562 template <
1563 typename SchemaDocumentType,
1564 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1565 typename StateAllocator = CrtAllocator>
1566 class GenericSchemaValidator :
1567 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1568 public internal::ISchemaValidator
1569 {
1570 public:
1571 typedef typename SchemaDocumentType::SchemaType SchemaType;
1572 typedef typename SchemaDocumentType::PointerType PointerType;
1573 typedef typename SchemaType::EncodingType EncodingType;
1574 typedef typename EncodingType::Ch Ch;
1575
1576 //! Constructor without output handler.
1577 /*!
1578 \param schemaDocument The schema document to conform to.
1579 \param allocator Optional allocator for storing internal validation states.
1580 \param schemaStackCapacity Optional initial capacity of schema path stack.
1581 \param documentStackCapacity Optional initial capacity of document path stack.
1582 */
1583 GenericSchemaValidator(
1584 const SchemaDocumentType& schemaDocument,
1585 StateAllocator* allocator = 0,
1586 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1587 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1588 :
1589 schemaDocument_(&schemaDocument),
1590 root_(schemaDocument.GetRoot()),
1591 stateAllocator_(allocator),
1592 ownStateAllocator_(0),
1593 schemaStack_(allocator, schemaStackCapacity),
1594 documentStack_(allocator, documentStackCapacity),
1595 outputHandler_(CreateNullHandler()),
1596 valid_(true)
1597 #if RAPIDJSON_SCHEMA_VERBOSE
1598 , depth_(0)
1599 #endif
1600 {
1601 }
1602
1603 //! Constructor with output handler.
1604 /*!
1605 \param schemaDocument The schema document to conform to.
1606 \param allocator Optional allocator for storing internal validation states.
1607 \param schemaStackCapacity Optional initial capacity of schema path stack.
1608 \param documentStackCapacity Optional initial capacity of document path stack.
1609 */
1610 GenericSchemaValidator(
1611 const SchemaDocumentType& schemaDocument,
1612 OutputHandler& outputHandler,
1613 StateAllocator* allocator = 0,
1614 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1615 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1616 :
1617 schemaDocument_(&schemaDocument),
1618 root_(schemaDocument.GetRoot()),
1619 stateAllocator_(allocator),
1620 ownStateAllocator_(0),
1621 schemaStack_(allocator, schemaStackCapacity),
1622 documentStack_(allocator, documentStackCapacity),
1623 outputHandler_(outputHandler),
1624 nullHandler_(0),
1625 valid_(true)
1626 #if RAPIDJSON_SCHEMA_VERBOSE
1627 , depth_(0)
1628 #endif
1629 {
1630 }
1631
1632 //! Destructor.
1633 ~GenericSchemaValidator() {
1634 Reset();
1635 if (nullHandler_) {
1636 nullHandler_->~OutputHandler();
1637 StateAllocator::Free(nullHandler_);
1638 }
1639 RAPIDJSON_DELETE(ownStateAllocator_);
1640 }
1641
1642 //! Reset the internal states.
1643 void Reset() {
1644 while (!schemaStack_.Empty())
1645 PopSchema();
1646 documentStack_.Clear();
1647 valid_ = true;
1648 }
1649
1650 //! Checks whether the current state is valid.
1651 // Implementation of ISchemaValidator
1652 virtual bool IsValid() const { return valid_; }
1653
1654 //! Gets the JSON pointer pointed to the invalid schema.
1655 PointerType GetInvalidSchemaPointer() const {
1656 return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
1657 }
1658
1659 //! Gets the keyword of invalid schema.
1660 const Ch* GetInvalidSchemaKeyword() const {
1661 return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1662 }
1663
1664 //! Gets the JSON pointer pointed to the invalid value.
1665 PointerType GetInvalidDocumentPointer() const {
1666 return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1667 }
1668
1669 #if RAPIDJSON_SCHEMA_VERBOSE
1670 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
1671 RAPIDJSON_MULTILINEMACRO_BEGIN\
1672 *documentStack_.template Push<Ch>() = '\0';\
1673 documentStack_.template Pop<Ch>(1);\
1674 internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
1675 RAPIDJSON_MULTILINEMACRO_END
1676 #else
1677 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
1678 #endif
1679
1680 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
1681 if (!valid_) return false; \
1682 if (!BeginValue() || !CurrentSchema().method arg1) {\
1683 RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
1684 return valid_ = false;\
1685 }
1686
1687 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
1688 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
1689 if (context->hasher)\
1690 static_cast<HasherType*>(context->hasher)->method arg2;\
1691 if (context->validators)\
1692 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
1693 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
1694 if (context->patternPropertiesValidators)\
1695 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
1696 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
1697 }
1698
1699 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
1700 return valid_ = EndValue() && outputHandler_.method arg2
1701
1702 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
1703 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
1704 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
1705 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
1706
1707 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
1708 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
1709 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
1710 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
1711 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
1712 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
1713 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
1714 bool RawNumber(const Ch* str, SizeType length, bool copy)
1715 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1716 bool String(const Ch* str, SizeType length, bool copy)
1717 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1718
1719 bool StartObject() {
1720 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
1721 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
1722 return valid_ = outputHandler_.StartObject();
1723 }
1724
1725 bool Key(const Ch* str, SizeType len, bool copy) {
1726 if (!valid_) return false;
1727 AppendToken(str, len);
1728 if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
1729 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
1730 return valid_ = outputHandler_.Key(str, len, copy);
1731 }
1732
1733 bool EndObject(SizeType memberCount) {
1734 if (!valid_) return false;
1735 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
1736 if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
1737 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
1738 }
1739
1740 bool StartArray() {
1741 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
1742 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
1743 return valid_ = outputHandler_.StartArray();
1744 }
1745
1746 bool EndArray(SizeType elementCount) {
1747 if (!valid_) return false;
1748 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
1749 if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
1750 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
1751 }
1752
1753 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
1754 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
1755 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
1756 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
1757
1758 // Implementation of ISchemaStateFactory<SchemaType>
1759 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
1760 return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
1761 #if RAPIDJSON_SCHEMA_VERBOSE
1762 depth_ + 1,
1763 #endif
1764 &GetStateAllocator());
1765 }
1766
1767 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
1768 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
1769 v->~GenericSchemaValidator();
1770 StateAllocator::Free(v);
1771 }
1772
1773 virtual void* CreateHasher() {
1774 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
1775 }
1776
1777 virtual uint64_t GetHashCode(void* hasher) {
1778 return static_cast<HasherType*>(hasher)->GetHashCode();
1779 }
1780
1781 virtual void DestroryHasher(void* hasher) {
1782 HasherType* h = static_cast<HasherType*>(hasher);
1783 h->~HasherType();
1784 StateAllocator::Free(h);
1785 }
1786
1787 virtual void* MallocState(size_t size) {
1788 return GetStateAllocator().Malloc(size);
1789 }
1790
1791 virtual void FreeState(void* p) {
1792 StateAllocator::Free(p);
1793 }
1794
1795 private:
1796 typedef typename SchemaType::Context Context;
1797 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
1798 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
1799
1800 GenericSchemaValidator(
1801 const SchemaDocumentType& schemaDocument,
1802 const SchemaType& root,
1803 #if RAPIDJSON_SCHEMA_VERBOSE
1804 unsigned depth,
1805 #endif
1806 StateAllocator* allocator = 0,
1807 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1808 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1809 :
1810 schemaDocument_(&schemaDocument),
1811 root_(root),
1812 stateAllocator_(allocator),
1813 ownStateAllocator_(0),
1814 schemaStack_(allocator, schemaStackCapacity),
1815 documentStack_(allocator, documentStackCapacity),
1816 outputHandler_(CreateNullHandler()),
1817 valid_(true)
1818 #if RAPIDJSON_SCHEMA_VERBOSE
1819 , depth_(depth)
1820 #endif
1821 {
1822 }
1823
1824 StateAllocator& GetStateAllocator() {
1825 if (!stateAllocator_)
1826 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
1827 return *stateAllocator_;
1828 }
1829
1830 bool BeginValue() {
1831 if (schemaStack_.Empty())
1832 PushSchema(root_);
1833 else {
1834 if (CurrentContext().inArray)
1835 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
1836
1837 if (!CurrentSchema().BeginValue(CurrentContext()))
1838 return false;
1839
1840 SizeType count = CurrentContext().patternPropertiesSchemaCount;
1841 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
1842 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
1843 bool valueUniqueness = CurrentContext().valueUniqueness;
1844 RAPIDJSON_ASSERT(CurrentContext().valueSchema);
1845 PushSchema(*CurrentContext().valueSchema);
1846
1847 if (count > 0) {
1848 CurrentContext().objectPatternValidatorType = patternValidatorType;
1849 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
1850 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
1851 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
1852 for (SizeType i = 0; i < count; i++)
1853 va[validatorCount++] = CreateSchemaValidator(*sa[i]);
1854 }
1855
1856 CurrentContext().arrayUniqueness = valueUniqueness;
1857 }
1858 return true;
1859 }
1860
1861 bool EndValue() {
1862 if (!CurrentSchema().EndValue(CurrentContext()))
1863 return false;
1864
1865 #if RAPIDJSON_SCHEMA_VERBOSE
1866 GenericStringBuffer<EncodingType> sb;
1867 schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
1868
1869 *documentStack_.template Push<Ch>() = '\0';
1870 documentStack_.template Pop<Ch>(1);
1871 internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
1872 #endif
1873
1874 uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
1875
1876 PopSchema();
1877
1878 if (!schemaStack_.Empty()) {
1879 Context& context = CurrentContext();
1880 if (context.valueUniqueness) {
1881 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
1882 if (!a)
1883 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
1884 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
1885 if (itr->GetUint64() == h)
1886 RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
1887 a->PushBack(h, GetStateAllocator());
1888 }
1889 }
1890
1891 // Remove the last token of document pointer
1892 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
1893 ;
1894
1895 return true;
1896 }
1897
1898 void AppendToken(const Ch* str, SizeType len) {
1899 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
1900 *documentStack_.template PushUnsafe<Ch>() = '/';
1901 for (SizeType i = 0; i < len; i++) {
1902 if (str[i] == '~') {
1903 *documentStack_.template PushUnsafe<Ch>() = '~';
1904 *documentStack_.template PushUnsafe<Ch>() = '0';
1905 }
1906 else if (str[i] == '/') {
1907 *documentStack_.template PushUnsafe<Ch>() = '~';
1908 *documentStack_.template PushUnsafe<Ch>() = '1';
1909 }
1910 else
1911 *documentStack_.template PushUnsafe<Ch>() = str[i];
1912 }
1913 }
1914
1915 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
1916
1917 RAPIDJSON_FORCEINLINE void PopSchema() {
1918 Context* c = schemaStack_.template Pop<Context>(1);
1919 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
1920 a->~HashCodeArray();
1921 StateAllocator::Free(a);
1922 }
1923 c->~Context();
1924 }
1925
1926 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
1927 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
1928 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
1929
1930 OutputHandler& CreateNullHandler() {
1931 return *(nullHandler_ = new (GetStateAllocator().Malloc(sizeof(OutputHandler))) OutputHandler);
1932 }
1933
1934 static const size_t kDefaultSchemaStackCapacity = 1024;
1935 static const size_t kDefaultDocumentStackCapacity = 256;
1936 const SchemaDocumentType* schemaDocument_;
1937 const SchemaType& root_;
1938 StateAllocator* stateAllocator_;
1939 StateAllocator* ownStateAllocator_;
1940 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
1941 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
1942 OutputHandler& outputHandler_;
1943 OutputHandler* nullHandler_;
1944 bool valid_;
1945 #if RAPIDJSON_SCHEMA_VERBOSE
1946 unsigned depth_;
1947 #endif
1948 };
1949
1950 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
1951
1952 ///////////////////////////////////////////////////////////////////////////////
1953 // SchemaValidatingReader
1954
1955 //! A helper class for parsing with validation.
1956 /*!
1957 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
1958
1959 \tparam parseFlags Combination of \ref ParseFlag.
1960 \tparam InputStream Type of input stream, implementing Stream concept.
1961 \tparam SourceEncoding Encoding of the input stream.
1962 \tparam SchemaDocumentType Type of schema document.
1963 \tparam StackAllocator Allocator type for stack.
1964 */
1965 template <
1966 unsigned parseFlags,
1967 typename InputStream,
1968 typename SourceEncoding,
1969 typename SchemaDocumentType = SchemaDocument,
1970 typename StackAllocator = CrtAllocator>
1971 class SchemaValidatingReader {
1972 public:
1973 typedef typename SchemaDocumentType::PointerType PointerType;
1974 typedef typename InputStream::Ch Ch;
1975
1976 //! Constructor
1977 /*!
1978 \param is Input stream.
1979 \param sd Schema document.
1980 */
1981 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
1982
1983 template <typename Handler>
1984 bool operator()(Handler& handler) {
1985 GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
1986 GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
1987 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
1988
1989 isValid_ = validator.IsValid();
1990 if (isValid_) {
1991 invalidSchemaPointer_ = PointerType();
1992 invalidSchemaKeyword_ = 0;
1993 invalidDocumentPointer_ = PointerType();
1994 }
1995 else {
1996 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
1997 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
1998 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
1999 }
2000
2001 return parseResult_;
2002 }
2003
2004 const ParseResult& GetParseResult() const { return parseResult_; }
2005 bool IsValid() const { return isValid_; }
2006 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
2007 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
2008 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
2009
2010 private:
2011 InputStream& is_;
2012 const SchemaDocumentType& sd_;
2013
2014 ParseResult parseResult_;
2015 PointerType invalidSchemaPointer_;
2016 const Ch* invalidSchemaKeyword_;
2017 PointerType invalidDocumentPointer_;
2018 bool isValid_;
2019 };
2020
2021 RAPIDJSON_NAMESPACE_END
2022 RAPIDJSON_DIAG_POP
2023
2024 #endif // RAPIDJSON_SCHEMA_H_
2025