RapidJSON: include/rapidjson/schema.h Source File

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