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