Arcane  v3.16.8.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
schema.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2// Tencent is pleased to support the open source community by making RapidJSON available->
3//
4// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
5//
6// Licensed under the MIT License (the "License"); you may not use this file except
7// in compliance with the License-> You may obtain a copy of the License at
8//
9// http://opensource->org/licenses/MIT
10//
11// Unless required by applicable law or agreed to in writing, software distributed
12// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
14// specific language governing permissions and limitations under the License->
15
16#ifndef RAPIDJSON_SCHEMA_H_
17#define RAPIDJSON_SCHEMA_H_
18
19#include "document.h"
20#include "pointer.h"
21#include "stringbuffer.h"
22#include "error/en.h"
23#include "uri.h"
24#include <cmath> // abs, floor
25
26#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
27#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
28#endif
29
30#if !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) || !(__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
31#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
32#endif
33
34#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
35#include "internal/regex.h"
36#elif RAPIDJSON_SCHEMA_USE_STDREGEX
37#include <regex>
38#endif
39
40#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
41#define RAPIDJSON_SCHEMA_HAS_REGEX 1
42#else
43#define RAPIDJSON_SCHEMA_HAS_REGEX 0
44#endif
45
46#ifndef RAPIDJSON_SCHEMA_VERBOSE
47#define RAPIDJSON_SCHEMA_VERBOSE 0
48#endif
49
50RAPIDJSON_DIAG_PUSH
51
52#if defined(__GNUC__)
53RAPIDJSON_DIAG_OFF(effc++)
54#endif
55
56#ifdef __clang__
57RAPIDJSON_DIAG_OFF(weak-vtables)
58RAPIDJSON_DIAG_OFF(exit-time-destructors)
59RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
60RAPIDJSON_DIAG_OFF(variadic-macros)
61#elif defined(_MSC_VER)
62RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
63#endif
64
66
68// Verbose Utilities
69
70#if RAPIDJSON_SCHEMA_VERBOSE
71
72namespace internal {
73
74inline void PrintInvalidKeywordData(const char* keyword) {
75 printf(" Fail keyword: '%s'\n", keyword);
76}
77
78inline void PrintInvalidKeywordData(const wchar_t* keyword) {
79 wprintf(L" Fail keyword: '%ls'\n", keyword);
80}
81
82inline void PrintInvalidDocumentData(const char* document) {
83 printf(" Fail document: '%s'\n", document);
84}
85
86inline void PrintInvalidDocumentData(const wchar_t* document) {
87 wprintf(L" Fail document: '%ls'\n", document);
88}
89
90inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) {
91 printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d);
92}
93
94inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) {
95 wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d);
96}
97
98inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) {
99 printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved);
100}
101
102inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) {
103 wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved);
104}
105
106inline void PrintMethodData(const char* method) {
107 printf("%s\n", method);
108}
109
110inline void PrintMethodData(const char* method, bool b) {
111 printf("%s, Data: '%s'\n", method, b ? "true" : "false");
112}
113
114inline void PrintMethodData(const char* method, int64_t i) {
115 printf("%s, Data: '%" PRId64 "'\n", method, i);
116}
117
118inline void PrintMethodData(const char* method, uint64_t u) {
119 printf("%s, Data: '%" PRIu64 "'\n", method, u);
120}
121
122inline void PrintMethodData(const char* method, double d) {
123 printf("%s, Data: '%lf'\n", method, d);
124}
125
126inline void PrintMethodData(const char* method, const char* s) {
127 printf("%s, Data: '%s'\n", method, s);
128}
129
130inline void PrintMethodData(const char* method, const wchar_t* s) {
131 wprintf(L"%hs, Data: '%ls'\n", method, s);
132}
133
134inline void PrintMethodData(const char* method, const char* s1, const char* s2) {
135 printf("%s, Data: '%s', '%s'\n", method, s1, s2);
136}
137
138inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) {
139 wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2);
140}
141
142} // namespace internal
143
144#endif // RAPIDJSON_SCHEMA_VERBOSE
145
146#ifndef RAPIDJSON_SCHEMA_PRINT
147#if RAPIDJSON_SCHEMA_VERBOSE
148#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__)
149#else
150#define RAPIDJSON_SCHEMA_PRINT(name, ...)
151#endif
152#endif
153
155// RAPIDJSON_INVALID_KEYWORD_RETURN
156
157#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
158RAPIDJSON_MULTILINEMACRO_BEGIN\
159 context.invalidCode = code;\
160 context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
161 RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\
162 return false;\
163RAPIDJSON_MULTILINEMACRO_END
164
166// ValidateFlag
167
174#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
175#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
176#endif
177
179enum ValidateFlag {
180 kValidateNoFlags = 0,
181 kValidateContinueOnErrorFlag = 1,
182 kValidateReadFlag = 2,
183 kValidateWriteFlag = 4,
184 kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS
185};
186
188// Specification
189enum SchemaDraft {
190 kDraftUnknown = -1,
191 kDraftNone = 0,
192 kDraft03 = 3,
193 kDraftMin = 4,
194 kDraft04 = 4,
195 kDraft05 = 5,
196 kDraftMax = 5,
197 kDraft06 = 6,
198 kDraft07 = 7,
199 kDraft2019_09 = 8,
200 kDraft2020_12 = 9
201};
202
203enum OpenApiVersion {
204 kVersionUnknown = -1,
205 kVersionNone = 0,
206 kVersionMin = 2,
207 kVersion20 = 2,
208 kVersion30 = 3,
209 kVersionMax = 3,
210 kVersion31 = 4,
211};
212
213struct Specification {
214 Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {}
215 Specification(OpenApiVersion o) : oapi(o) {
216 if (oapi == kVersion20) draft = kDraft04;
217 else if (oapi == kVersion30) draft = kDraft05;
218 else if (oapi == kVersion31) draft = kDraft2020_12;
219 else draft = kDraft04;
220 }
221 ~Specification() {}
222 bool IsSupported() const {
223 return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax)));
224 }
225 SchemaDraft draft;
226 OpenApiVersion oapi;
227};
228
230// Forward declarations
231
232template <typename ValueType, typename Allocator>
234
235namespace internal {
236
237template <typename SchemaDocumentType>
238class Schema;
239
241// ISchemaValidator
242
244public:
245 virtual ~ISchemaValidator() {}
246 virtual bool IsValid() const = 0;
247 virtual void SetValidateFlags(unsigned flags) = 0;
248 virtual unsigned GetValidateFlags() const = 0;
249};
250
252// ISchemaStateFactory
253
254template <typename SchemaType>
256public:
257 virtual ~ISchemaStateFactory() {}
258 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
259 virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
260 virtual void* CreateHasher() = 0;
261 virtual uint64_t GetHashCode(void* hasher) = 0;
262 virtual void DestroryHasher(void* hasher) = 0;
263 virtual void* MallocState(size_t size) = 0;
264 virtual void FreeState(void* p) = 0;
265};
266
268// IValidationErrorHandler
269
270template <typename SchemaType>
272public:
273 typedef typename SchemaType::Ch Ch;
274 typedef typename SchemaType::SValue SValue;
275
276 virtual ~IValidationErrorHandler() {}
277
278 virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
279 virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
280 virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
281 virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
282 virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
283 virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
284 virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
285 virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
286 virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
287
288 virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
289 virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
290 virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
291
292 virtual void DisallowedItem(SizeType index) = 0;
293 virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
294 virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
295 virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
296
297 virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
298 virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
299 virtual void StartMissingProperties() = 0;
300 virtual void AddMissingProperty(const SValue& name) = 0;
301 virtual bool EndMissingProperties() = 0;
302 virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
303 virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
304
305 virtual void StartDependencyErrors() = 0;
306 virtual void StartMissingDependentProperties() = 0;
307 virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
308 virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
309 virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
310 virtual bool EndDependencyErrors() = 0;
311
312 virtual void DisallowedValue(const ValidateErrorCode code) = 0;
313 virtual void StartDisallowedType() = 0;
314 virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
315 virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
316 virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
317 virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
318 virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
319 virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0;
320 virtual void Disallowed() = 0;
321 virtual void DisallowedWhenWriting() = 0;
322 virtual void DisallowedWhenReading() = 0;
323};
324
325
327// Hasher
328
329// For comparison of compound value
330template<typename Encoding, typename Allocator>
331class Hasher {
332public:
333 typedef typename Encoding::Ch Ch;
334
335 Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
336
337 bool Null() { return WriteType(kNullType); }
338 bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
339 bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
340 bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
341 bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
342 bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
343 bool Double(double d) {
344 Number n;
345 if (d < 0) n.u.i = static_cast<int64_t>(d);
346 else n.u.u = static_cast<uint64_t>(d);
347 n.d = d;
348 return WriteNumber(n);
349 }
350
351 bool RawNumber(const Ch* str, SizeType len, bool) {
352 WriteBuffer(kNumberType, str, len * sizeof(Ch));
353 return true;
354 }
355
356 bool String(const Ch* str, SizeType len, bool) {
357 WriteBuffer(kStringType, str, len * sizeof(Ch));
358 return true;
359 }
360
361 bool StartObject() { return true; }
362 bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
363 bool EndObject(SizeType memberCount) {
364 uint64_t h = Hash(0, kObjectType);
365 uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
366 for (SizeType i = 0; i < memberCount; i++)
367 // Issue #2205
368 // Hasing the key to avoid key=value cases with bug-prone zero-value hash
369 h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]); // Use xor to achieve member order insensitive
370 *stack_.template Push<uint64_t>() = h;
371 return true;
372 }
373
374 bool StartArray() { return true; }
375 bool EndArray(SizeType elementCount) {
376 uint64_t h = Hash(0, kArrayType);
377 uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
378 for (SizeType i = 0; i < elementCount; i++)
379 h = Hash(h, e[i]); // Use hash to achieve element order sensitive
380 *stack_.template Push<uint64_t>() = h;
381 return true;
382 }
383
384 bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
385
386 uint64_t GetHashCode() const {
387 RAPIDJSON_ASSERT(IsValid());
388 return *stack_.template Top<uint64_t>();
389 }
390
391private:
392 static const size_t kDefaultSize = 256;
393 struct Number {
394 union U {
395 uint64_t u;
396 int64_t i;
397 }u;
398 double d;
399 };
400
401 bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
402
403 bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
404
405 bool WriteBuffer(Type type, const void* data, size_t len) {
406 // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
407 uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type);
408 const unsigned char* d = static_cast<const unsigned char*>(data);
409 for (size_t i = 0; i < len; i++)
410 h = Hash(h, d[i]);
411 *stack_.template Push<uint64_t>() = h;
412 return true;
413 }
414
415 static uint64_t Hash(uint64_t h, uint64_t d) {
416 static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
417 h ^= d;
418 h *= kPrime;
419 return h;
420 }
421
422 Stack<Allocator> stack_;
423};
424
426// SchemaValidationContext
427
428template <typename SchemaDocumentType>
429struct SchemaValidationContext {
430 typedef Schema<SchemaDocumentType> SchemaType;
431 typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
432 typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
433 typedef typename SchemaType::ValueType ValueType;
434 typedef typename ValueType::Ch Ch;
435
436 enum PatternValidatorType {
437 kPatternValidatorOnly,
438 kPatternValidatorWithProperty,
439 kPatternValidatorWithAdditionalProperty
440 };
441
442 SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) :
443 factory(f),
444 error_handler(eh),
445 schema(s),
446 flags(fl),
447 valueSchema(),
448 invalidKeyword(),
449 invalidCode(),
450 hasher(),
451 arrayElementHashCodes(),
452 validators(),
453 validatorCount(),
454 patternPropertiesValidators(),
455 patternPropertiesValidatorCount(),
456 patternPropertiesSchemas(),
457 patternPropertiesSchemaCount(),
458 valuePatternValidatorType(kPatternValidatorOnly),
459 propertyExist(),
460 inArray(false),
461 valueUniqueness(false),
462 arrayUniqueness(false)
463 {
464 }
465
466 ~SchemaValidationContext() {
467 if (hasher)
468 factory.DestroryHasher(hasher);
469 if (validators) {
470 for (SizeType i = 0; i < validatorCount; i++) {
471 if (validators[i]) {
472 factory.DestroySchemaValidator(validators[i]);
473 }
474 }
475 factory.FreeState(validators);
476 }
477 if (patternPropertiesValidators) {
478 for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) {
479 if (patternPropertiesValidators[i]) {
480 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
481 }
482 }
483 factory.FreeState(patternPropertiesValidators);
484 }
485 if (patternPropertiesSchemas)
486 factory.FreeState(patternPropertiesSchemas);
487 if (propertyExist)
488 factory.FreeState(propertyExist);
489 }
490
491 SchemaValidatorFactoryType& factory;
492 ErrorHandlerType& error_handler;
493 const SchemaType* schema;
494 unsigned flags;
495 const SchemaType* valueSchema;
496 const Ch* invalidKeyword;
497 ValidateErrorCode invalidCode;
498 void* hasher; // Only validator access
499 void* arrayElementHashCodes; // Only validator access this
500 ISchemaValidator** validators;
501 SizeType validatorCount;
502 ISchemaValidator** patternPropertiesValidators;
503 SizeType patternPropertiesValidatorCount;
504 const SchemaType** patternPropertiesSchemas;
505 SizeType patternPropertiesSchemaCount;
506 PatternValidatorType valuePatternValidatorType;
507 PatternValidatorType objectPatternValidatorType;
508 SizeType arrayElementIndex;
509 bool* propertyExist;
510 bool inArray;
511 bool valueUniqueness;
512 bool arrayUniqueness;
513};
514
516// Schema
517
518template <typename SchemaDocumentType>
519class Schema {
520public:
521 typedef typename SchemaDocumentType::ValueType ValueType;
522 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
523 typedef typename SchemaDocumentType::PointerType PointerType;
524 typedef typename ValueType::EncodingType EncodingType;
525 typedef typename EncodingType::Ch Ch;
527 typedef Schema<SchemaDocumentType> SchemaType;
529 typedef IValidationErrorHandler<Schema> ErrorHandler;
531 friend class GenericSchemaDocument<ValueType, AllocatorType>;
532
533 Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
534 allocator_(allocator),
535 uri_(schemaDocument->GetURI(), *allocator),
536 id_(id, allocator),
537 spec_(schemaDocument->GetSpecification()),
538 pointer_(p, allocator),
539 typeless_(schemaDocument->GetTypeless()),
540 enum_(),
541 enumCount_(),
542 not_(),
543 type_((1 << kTotalSchemaType) - 1), // typeless
544 validatorCount_(),
545 notValidatorIndex_(),
546 properties_(),
547 additionalPropertiesSchema_(),
548 patternProperties_(),
549 patternPropertyCount_(),
550 propertyCount_(),
551 minProperties_(),
552 maxProperties_(SizeType(~0)),
553 additionalProperties_(true),
554 hasDependencies_(),
555 hasRequired_(),
556 hasSchemaDependencies_(),
557 additionalItemsSchema_(),
558 itemsList_(),
559 itemsTuple_(),
560 itemsTupleCount_(),
561 minItems_(),
562 maxItems_(SizeType(~0)),
563 additionalItems_(true),
564 uniqueItems_(false),
565 pattern_(),
566 minLength_(0),
567 maxLength_(~SizeType(0)),
568 exclusiveMinimum_(false),
569 exclusiveMaximum_(false),
570 defaultValueLength_(0),
571 readOnly_(false),
572 writeOnly_(false),
573 nullable_(false)
574 {
576 p.StringifyUriFragment(sb);
577 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString());
578
579 typedef typename ValueType::ConstValueIterator ConstValueIterator;
580 typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
581
582 // PR #1393
583 // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite
584 // recursion (with recursive schemas), since schemaDocument->getSchema() is always
585 // checked before creating a new one. Don't cache typeless_, though.
586 if (this != typeless_) {
587 typedef typename SchemaDocumentType::SchemaEntry SchemaEntry;
588 SchemaEntry *entry = schemaDocument->schemaMap_.template Push<SchemaEntry>();
589 new (entry) SchemaEntry(pointer_, this, true, allocator_);
590 schemaDocument->AddSchemaRefs(this);
591 }
592
593 if (!value.IsObject())
594 return;
595
596 // If we have an id property, resolve it with the in-scope id
597 // Not supported for open api 2.0 or 3.0
598 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
599 if (const ValueType* v = GetMember(value, GetIdString())) {
600 if (v->IsString()) {
601 UriType local(*v, allocator);
602 id_ = local.Resolve(id_, allocator);
603 RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString());
604 }
605 }
606
607 if (const ValueType* v = GetMember(value, GetTypeString())) {
608 type_ = 0;
609 if (v->IsString())
610 AddType(*v);
611 else if (v->IsArray())
612 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
613 AddType(*itr);
614 }
615
616 if (const ValueType* v = GetMember(value, GetEnumString())) {
617 if (v->IsArray() && v->Size() > 0) {
618 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
619 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
621 char buffer[256u + 24];
622 MemoryPoolAllocator<AllocatorType> hasherAllocator(buffer, sizeof(buffer));
623 EnumHasherType h(&hasherAllocator, 256);
624 itr->Accept(h);
625 enum_[enumCount_++] = h.GetHashCode();
626 }
627 }
628 }
629
630 if (schemaDocument)
631 AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
632
633 // AnyOf, OneOf, Not not supported for open api 2.0
634 if (schemaDocument && spec_.oapi != kVersion20) {
635 AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
636 AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
637
638 if (const ValueType* v = GetMember(value, GetNotString())) {
639 schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document, id_);
640 notValidatorIndex_ = validatorCount_;
641 validatorCount_++;
642 }
643 }
644
645 // Object
646
647 const ValueType* properties = GetMember(value, GetPropertiesString());
648 const ValueType* required = GetMember(value, GetRequiredString());
649 const ValueType* dependencies = GetMember(value, GetDependenciesString());
650 {
651 // Gather properties from properties/required/dependencies
652 SValue allProperties(kArrayType);
653
654 if (properties && properties->IsObject())
655 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
656 AddUniqueElement(allProperties, itr->name);
657
658 if (required && required->IsArray())
659 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
660 if (itr->IsString())
661 AddUniqueElement(allProperties, *itr);
662
663 // Dependencies not supported for open api 2.0 and 3.0
664 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
665 if (dependencies && dependencies->IsObject())
666 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
667 AddUniqueElement(allProperties, itr->name);
668 if (itr->value.IsArray())
669 for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
670 if (i->IsString())
671 AddUniqueElement(allProperties, *i);
672 }
673
674 if (allProperties.Size() > 0) {
675 propertyCount_ = allProperties.Size();
676 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
677 for (SizeType i = 0; i < propertyCount_; i++) {
678 new (&properties_[i]) Property();
679 properties_[i].name = allProperties[i];
680 properties_[i].schema = typeless_;
681 }
682 }
683 }
684
685 if (properties && properties->IsObject()) {
686 PointerType q = p.Append(GetPropertiesString(), allocator_);
687 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
688 SizeType index;
689 if (FindPropertyIndex(itr->name, &index))
690 schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
691 }
692 }
693
694 // PatternProperties not supported for open api 2.0 and 3.0
695 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
696 if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
697 PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
698 patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
699 patternPropertyCount_ = 0;
700
701 for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
702 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
703 PointerType r = q.Append(itr->name, allocator_);
704 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r);
705 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_);
706 patternPropertyCount_++;
707 }
708 }
709
710 if (required && required->IsArray())
711 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
712 if (itr->IsString()) {
713 SizeType index;
714 if (FindPropertyIndex(*itr, &index)) {
715 properties_[index].required = true;
716 hasRequired_ = true;
717 }
718 }
719
720 // Dependencies not supported for open api 2.0 and 3.0
721 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
722 if (dependencies && dependencies->IsObject()) {
723 PointerType q = p.Append(GetDependenciesString(), allocator_);
724 hasDependencies_ = true;
725 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
726 SizeType sourceIndex;
727 if (FindPropertyIndex(itr->name, &sourceIndex)) {
728 if (itr->value.IsArray()) {
729 properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
730 std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
731 for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
732 SizeType targetIndex;
733 if (FindPropertyIndex(*targetItr, &targetIndex))
734 properties_[sourceIndex].dependencies[targetIndex] = true;
735 }
736 }
737 else if (itr->value.IsObject()) {
738 hasSchemaDependencies_ = true;
739 schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_);
740 properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
741 validatorCount_++;
742 }
743 }
744 }
745 }
746
747 if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
748 if (v->IsBool())
749 additionalProperties_ = v->GetBool();
750 else if (v->IsObject())
751 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_);
752 }
753
754 AssignIfExist(minProperties_, value, GetMinPropertiesString());
755 AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
756
757 // Array
758 if (const ValueType* v = GetMember(value, GetItemsString())) {
759 PointerType q = p.Append(GetItemsString(), allocator_);
760 if (v->IsObject()) // List validation
761 schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_);
762 else if (v->IsArray()) { // Tuple validation
763 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
764 SizeType index = 0;
765 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
766 schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_);
767 }
768 }
769
770 AssignIfExist(minItems_, value, GetMinItemsString());
771 AssignIfExist(maxItems_, value, GetMaxItemsString());
772
773 // AdditionalItems not supported for openapi 2.0 and 3.0
774 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
775 if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
776 if (v->IsBool())
777 additionalItems_ = v->GetBool();
778 else if (v->IsObject())
779 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_);
780 }
781
782 AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
783
784 // String
785 AssignIfExist(minLength_, value, GetMinLengthString());
786 AssignIfExist(maxLength_, value, GetMaxLengthString());
787
788 if (const ValueType* v = GetMember(value, GetPatternString()))
789 pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_));
790
791 // Number
792 if (const ValueType* v = GetMember(value, GetMinimumString()))
793 if (v->IsNumber())
794 minimum_.CopyFrom(*v, *allocator_);
795
796 if (const ValueType* v = GetMember(value, GetMaximumString()))
797 if (v->IsNumber())
798 maximum_.CopyFrom(*v, *allocator_);
799
800 AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
801 AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
802
803 if (const ValueType* v = GetMember(value, GetMultipleOfString()))
804 if (v->IsNumber() && v->GetDouble() > 0.0)
805 multipleOf_.CopyFrom(*v, *allocator_);
806
807 // Default
808 if (const ValueType* v = GetMember(value, GetDefaultValueString()))
809 if (v->IsString())
810 defaultValueLength_ = v->GetStringLength();
811
812 // ReadOnly - open api only (until draft 7 supported)
813 // WriteOnly - open api 3 only (until draft 7 supported)
814 // Both can't be true
815 if (spec_.oapi != kVersionNone)
816 AssignIfExist(readOnly_, value, GetReadOnlyString());
817 if (spec_.oapi >= kVersion30)
818 AssignIfExist(writeOnly_, value, GetWriteOnlyString());
819 if (readOnly_ && writeOnly_)
820 schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p);
821
822 // Nullable - open api 3 only
823 // If true add 'null' as allowable type
824 if (spec_.oapi >= kVersion30) {
825 AssignIfExist(nullable_, value, GetNullableString());
826 if (nullable_)
827 AddType(GetNullString());
828 }
829 }
830
831 ~Schema() {
832 AllocatorType::Free(enum_);
833 if (properties_) {
834 for (SizeType i = 0; i < propertyCount_; i++)
835 properties_[i].~Property();
836 AllocatorType::Free(properties_);
837 }
838 if (patternProperties_) {
839 for (SizeType i = 0; i < patternPropertyCount_; i++)
840 patternProperties_[i].~PatternProperty();
841 AllocatorType::Free(patternProperties_);
842 }
843 AllocatorType::Free(itemsTuple_);
844#if RAPIDJSON_SCHEMA_HAS_REGEX
845 if (pattern_) {
846 pattern_->~RegexType();
847 AllocatorType::Free(pattern_);
848 }
849#endif
850 }
851
852 const SValue& GetURI() const {
853 return uri_;
854 }
855
856 const UriType& GetId() const {
857 return id_;
858 }
859
860 const Specification& GetSpecification() const {
861 return spec_;
862 }
863
864 const PointerType& GetPointer() const {
865 return pointer_;
866 }
867
868 bool BeginValue(Context& context) const {
869 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue");
870 if (context.inArray) {
871 if (uniqueItems_)
872 context.valueUniqueness = true;
873
874 if (itemsList_)
875 context.valueSchema = itemsList_;
876 else if (itemsTuple_) {
877 if (context.arrayElementIndex < itemsTupleCount_)
878 context.valueSchema = itemsTuple_[context.arrayElementIndex];
879 else if (additionalItemsSchema_)
880 context.valueSchema = additionalItemsSchema_;
881 else if (additionalItems_)
882 context.valueSchema = typeless_;
883 else {
884 context.error_handler.DisallowedItem(context.arrayElementIndex);
885 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
886 context.valueSchema = typeless_;
887 // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
888 context.arrayElementIndex++;
889 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
890 }
891 }
892 else
893 context.valueSchema = typeless_;
894
895 context.arrayElementIndex++;
896 }
897 return true;
898 }
899
900 RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
901 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue");
902 // Only check pattern properties if we have validators
903 if (context.patternPropertiesValidatorCount > 0) {
904 bool otherValid = false;
905 SizeType count = context.patternPropertiesValidatorCount;
906 if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
907 otherValid = context.patternPropertiesValidators[--count]->IsValid();
908
909 bool patternValid = true;
910 for (SizeType i = 0; i < count; i++)
911 if (!context.patternPropertiesValidators[i]->IsValid()) {
912 patternValid = false;
913 break;
914 }
915
916 if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
917 if (!patternValid) {
918 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
919 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
920 }
921 }
922 else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
923 if (!patternValid || !otherValid) {
924 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
925 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
926 }
927 }
928 else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
929 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
930 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
931 }
932 }
933
934 // For enums only check if we have a hasher
935 if (enum_ && context.hasher) {
936 const uint64_t h = context.factory.GetHashCode(context.hasher);
937 for (SizeType i = 0; i < enumCount_; i++)
938 if (enum_[i] == h)
939 goto foundEnum;
940 context.error_handler.DisallowedValue(kValidateErrorEnum);
941 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
942 foundEnum:;
943 }
944
945 // Only check allOf etc if we have validators
946 if (context.validatorCount > 0) {
947 if (allOf_.schemas)
948 for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
949 if (!context.validators[i]->IsValid()) {
950 context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
951 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
952 }
953
954 if (anyOf_.schemas) {
955 for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
956 if (context.validators[i]->IsValid())
957 goto foundAny;
958 context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
959 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
960 foundAny:;
961 }
962
963 if (oneOf_.schemas) {
964 bool oneValid = false;
965 SizeType firstMatch = 0;
966 for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
967 if (context.validators[i]->IsValid()) {
968 if (oneValid) {
969 context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin);
970 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
971 } else {
972 oneValid = true;
973 firstMatch = i - oneOf_.begin;
974 }
975 }
976 if (!oneValid) {
977 context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
978 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
979 }
980 }
981
982 if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
983 context.error_handler.Disallowed();
984 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
985 }
986 }
987
988 return true;
989 }
990
991 bool Null(Context& context) const {
992 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null");
993 if (!(type_ & (1 << kNullSchemaType))) {
994 DisallowedType(context, GetNullString());
995 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
996 }
997 return CreateParallelValidator(context);
998 }
999
1000 bool Bool(Context& context, bool b) const {
1001 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b);
1002 if (!CheckBool(context, b))
1003 return false;
1004 return CreateParallelValidator(context);
1005 }
1006
1007 bool Int(Context& context, int i) const {
1008 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i);
1009 if (!CheckInt(context, i))
1010 return false;
1011 return CreateParallelValidator(context);
1012 }
1013
1014 bool Uint(Context& context, unsigned u) const {
1015 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u);
1016 if (!CheckUint(context, u))
1017 return false;
1018 return CreateParallelValidator(context);
1019 }
1020
1021 bool Int64(Context& context, int64_t i) const {
1022 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i);
1023 if (!CheckInt(context, i))
1024 return false;
1025 return CreateParallelValidator(context);
1026 }
1027
1028 bool Uint64(Context& context, uint64_t u) const {
1029 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u);
1030 if (!CheckUint(context, u))
1031 return false;
1032 return CreateParallelValidator(context);
1033 }
1034
1035 bool Double(Context& context, double d) const {
1036 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d);
1037 if (!(type_ & (1 << kNumberSchemaType))) {
1038 DisallowedType(context, GetNumberString());
1039 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1040 }
1041
1042 if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
1043 return false;
1044
1045 if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
1046 return false;
1047
1048 if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
1049 return false;
1050
1051 return CreateParallelValidator(context);
1052 }
1053
1054 bool String(Context& context, const Ch* str, SizeType length, bool) const {
1055 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str);
1056 if (!(type_ & (1 << kStringSchemaType))) {
1057 DisallowedType(context, GetStringString());
1058 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1059 }
1060
1061 if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
1062 SizeType count;
1063 if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
1064 if (count < minLength_) {
1065 context.error_handler.TooShort(str, length, minLength_);
1066 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
1067 }
1068 if (count > maxLength_) {
1069 context.error_handler.TooLong(str, length, maxLength_);
1070 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
1071 }
1072 }
1073 }
1074
1075 if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
1076 context.error_handler.DoesNotMatch(str, length);
1077 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
1078 }
1079
1080 return CreateParallelValidator(context);
1081 }
1082
1083 bool StartObject(Context& context) const {
1084 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject");
1085 if (!(type_ & (1 << kObjectSchemaType))) {
1086 DisallowedType(context, GetObjectString());
1087 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1088 }
1089
1090 if (hasDependencies_ || hasRequired_) {
1091 context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
1092 std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
1093 }
1094
1095 if (patternProperties_) { // pre-allocate schema array
1096 SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
1097 context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
1098 context.patternPropertiesSchemaCount = 0;
1099 std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
1100 }
1101
1102 return CreateParallelValidator(context);
1103 }
1104
1105 bool Key(Context& context, const Ch* str, SizeType len, bool) const {
1106 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str);
1107
1108 if (patternProperties_) {
1109 context.patternPropertiesSchemaCount = 0;
1110 for (SizeType i = 0; i < patternPropertyCount_; i++)
1111 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
1112 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
1113 context.valueSchema = typeless_;
1114 }
1115 }
1116
1117 SizeType index = 0;
1118 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
1119 if (context.patternPropertiesSchemaCount > 0) {
1120 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
1121 context.valueSchema = typeless_;
1122 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
1123 }
1124 else
1125 context.valueSchema = properties_[index].schema;
1126
1127 if (context.propertyExist)
1128 context.propertyExist[index] = true;
1129
1130 return true;
1131 }
1132
1133 if (additionalPropertiesSchema_) {
1134 if (context.patternPropertiesSchemaCount > 0) {
1135 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
1136 context.valueSchema = typeless_;
1137 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
1138 }
1139 else
1140 context.valueSchema = additionalPropertiesSchema_;
1141 return true;
1142 }
1143 else if (additionalProperties_) {
1144 context.valueSchema = typeless_;
1145 return true;
1146 }
1147
1148 if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
1149 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
1150 context.valueSchema = typeless_;
1151 context.error_handler.DisallowedProperty(str, len);
1152 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
1153 }
1154
1155 return true;
1156 }
1157
1158 bool EndObject(Context& context, SizeType memberCount) const {
1159 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject");
1160 if (hasRequired_) {
1161 context.error_handler.StartMissingProperties();
1162 for (SizeType index = 0; index < propertyCount_; index++)
1163 if (properties_[index].required && !context.propertyExist[index])
1164 if (properties_[index].schema->defaultValueLength_ == 0 )
1165 context.error_handler.AddMissingProperty(properties_[index].name);
1166 if (context.error_handler.EndMissingProperties())
1167 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
1168 }
1169
1170 if (memberCount < minProperties_) {
1171 context.error_handler.TooFewProperties(memberCount, minProperties_);
1172 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
1173 }
1174
1175 if (memberCount > maxProperties_) {
1176 context.error_handler.TooManyProperties(memberCount, maxProperties_);
1177 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
1178 }
1179
1180 if (hasDependencies_) {
1181 context.error_handler.StartDependencyErrors();
1182 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
1183 const Property& source = properties_[sourceIndex];
1184 if (context.propertyExist[sourceIndex]) {
1185 if (source.dependencies) {
1186 context.error_handler.StartMissingDependentProperties();
1187 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
1188 if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
1189 context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
1190 context.error_handler.EndMissingDependentProperties(source.name);
1191 }
1192 else if (source.dependenciesSchema) {
1193 ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
1194 if (!dependenciesValidator->IsValid())
1195 context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
1196 }
1197 }
1198 }
1199 if (context.error_handler.EndDependencyErrors())
1200 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
1201 }
1202
1203 return true;
1204 }
1205
1206 bool StartArray(Context& context) const {
1207 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray");
1208 context.arrayElementIndex = 0;
1209 context.inArray = true; // Ensure we note that we are in an array
1210
1211 if (!(type_ & (1 << kArraySchemaType))) {
1212 DisallowedType(context, GetArrayString());
1213 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1214 }
1215
1216 return CreateParallelValidator(context);
1217 }
1218
1219 bool EndArray(Context& context, SizeType elementCount) const {
1220 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray");
1221 context.inArray = false;
1222
1223 if (elementCount < minItems_) {
1224 context.error_handler.TooFewItems(elementCount, minItems_);
1225 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
1226 }
1227
1228 if (elementCount > maxItems_) {
1229 context.error_handler.TooManyItems(elementCount, maxItems_);
1230 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
1231 }
1232
1233 return true;
1234 }
1235
1236 static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
1237 switch (validateErrorCode) {
1238 case kValidateErrorMultipleOf: return GetMultipleOfString();
1239 case kValidateErrorMaximum: return GetMaximumString();
1240 case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
1241 case kValidateErrorMinimum: return GetMinimumString();
1242 case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
1243
1244 case kValidateErrorMaxLength: return GetMaxLengthString();
1245 case kValidateErrorMinLength: return GetMinLengthString();
1246 case kValidateErrorPattern: return GetPatternString();
1247
1248 case kValidateErrorMaxItems: return GetMaxItemsString();
1249 case kValidateErrorMinItems: return GetMinItemsString();
1250 case kValidateErrorUniqueItems: return GetUniqueItemsString();
1251 case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
1252
1253 case kValidateErrorMaxProperties: return GetMaxPropertiesString();
1254 case kValidateErrorMinProperties: return GetMinPropertiesString();
1255 case kValidateErrorRequired: return GetRequiredString();
1256 case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
1257 case kValidateErrorPatternProperties: return GetPatternPropertiesString();
1258 case kValidateErrorDependencies: return GetDependenciesString();
1259
1260 case kValidateErrorEnum: return GetEnumString();
1261 case kValidateErrorType: return GetTypeString();
1262
1263 case kValidateErrorOneOf: return GetOneOfString();
1264 case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
1265 case kValidateErrorAllOf: return GetAllOfString();
1266 case kValidateErrorAnyOf: return GetAnyOfString();
1267 case kValidateErrorNot: return GetNotString();
1268
1269 case kValidateErrorReadOnly: return GetReadOnlyString();
1270 case kValidateErrorWriteOnly: return GetWriteOnlyString();
1271
1272 default: return GetNullString();
1273 }
1274 }
1275
1276
1277 // Generate functions for string literal according to Ch
1278#define RAPIDJSON_STRING_(name, ...) \
1279 static const ValueType& Get##name##String() {\
1280 static const Ch s[] = { __VA_ARGS__, '\0' };\
1281 static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
1282 return v;\
1283 }
1284
1285 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
1286 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
1287 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
1288 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
1289 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
1290 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
1291 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
1292 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
1293 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
1294 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
1295 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
1296 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
1297 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
1298 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1299 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
1300 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
1301 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1302 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1303 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1304 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1305 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
1306 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
1307 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
1308 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
1309 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
1310 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
1311 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
1312 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
1313 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
1314 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
1315 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
1316 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
1317 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
1318 RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
1319 RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a')
1320 RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
1321 RAPIDJSON_STRING_(Id, 'i', 'd')
1322 RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r')
1323 RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i')
1324 RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y')
1325 RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
1326 RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e')
1327
1328#undef RAPIDJSON_STRING_
1329
1330private:
1331 enum SchemaValueType {
1332 kNullSchemaType,
1333 kBooleanSchemaType,
1334 kObjectSchemaType,
1335 kArraySchemaType,
1336 kStringSchemaType,
1337 kNumberSchemaType,
1338 kIntegerSchemaType,
1339 kTotalSchemaType
1340 };
1341
1342#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1344#elif RAPIDJSON_SCHEMA_USE_STDREGEX
1345 typedef std::basic_regex<Ch> RegexType;
1346#else
1347 typedef char RegexType;
1348#endif
1349
1350 struct SchemaArray {
1351 SchemaArray() : schemas(), count() {}
1352 ~SchemaArray() { AllocatorType::Free(schemas); }
1353 const SchemaType** schemas;
1354 SizeType begin; // begin index of context.validators
1355 SizeType count;
1356 };
1357
1358 template <typename V1, typename V2>
1359 void AddUniqueElement(V1& a, const V2& v) {
1360 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
1361 if (*itr == v)
1362 return;
1363 V1 c(v, *allocator_);
1364 a.PushBack(c, *allocator_);
1365 }
1366
1367 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
1368 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
1369 return itr != value.MemberEnd() ? &(itr->value) : 0;
1370 }
1371
1372 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
1373 if (const ValueType* v = GetMember(value, name))
1374 if (v->IsBool())
1375 out = v->GetBool();
1376 }
1377
1378 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
1379 if (const ValueType* v = GetMember(value, name))
1380 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
1381 out = static_cast<SizeType>(v->GetUint64());
1382 }
1383
1384 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
1385 if (const ValueType* v = GetMember(value, name)) {
1386 if (v->IsArray() && v->Size() > 0) {
1387 PointerType q = p.Append(name, allocator_);
1388 out.count = v->Size();
1389 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
1390 memset(out.schemas, 0, sizeof(Schema*)* out.count);
1391 for (SizeType i = 0; i < out.count; i++)
1392 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_);
1393 out.begin = validatorCount_;
1394 validatorCount_ += out.count;
1395 }
1396 }
1397 }
1398
1399#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1400 template <typename ValueType>
1401 RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
1402 if (value.IsString()) {
1403 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
1404 if (!r->IsValid()) {
1405 sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
1406 r->~RegexType();
1407 AllocatorType::Free(r);
1408 r = 0;
1409 }
1410 return r;
1411 }
1412 return 0;
1413 }
1414
1415 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1416 GenericRegexSearch<RegexType> rs(*pattern);
1417 return rs.Search(str);
1418 }
1419#elif RAPIDJSON_SCHEMA_USE_STDREGEX
1420 template <typename ValueType>
1421 RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
1422 if (value.IsString()) {
1423 RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
1424 try {
1425 return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1426 }
1427 catch (const std::regex_error& e) {
1428 sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
1429 AllocatorType::Free(r);
1430 }
1431 }
1432 return 0;
1433 }
1434
1435 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1436 std::match_results<const Ch*> r;
1437 return std::regex_search(str, str + length, r, *pattern);
1438 }
1439#else
1440 template <typename ValueType>
1441 RegexType* CreatePattern(const ValueType&) {
1442 return 0;
1443 }
1444
1445 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1446#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1447
1448 void AddType(const ValueType& type) {
1449 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1450 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1451 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1452 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1453 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1454 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1455 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1456 }
1457
1458 // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required.
1459 // Also creates a hasher for enums and array uniqueness, if required.
1460 // Also a useful place to add type-independent error checks.
1461 bool CreateParallelValidator(Context& context) const {
1462 if (enum_ || context.arrayUniqueness)
1463 context.hasher = context.factory.CreateHasher();
1464
1465 if (validatorCount_) {
1466 RAPIDJSON_ASSERT(context.validators == 0);
1467 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1468 std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_);
1469 context.validatorCount = validatorCount_;
1470
1471 // Always return after first failure for these sub-validators
1472 if (allOf_.schemas)
1473 CreateSchemaValidators(context, allOf_, false);
1474
1475 if (anyOf_.schemas)
1476 CreateSchemaValidators(context, anyOf_, false);
1477
1478 if (oneOf_.schemas)
1479 CreateSchemaValidators(context, oneOf_, false);
1480
1481 if (not_)
1482 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
1483
1484 if (hasSchemaDependencies_) {
1485 for (SizeType i = 0; i < propertyCount_; i++)
1486 if (properties_[i].dependenciesSchema)
1487 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
1488 }
1489 }
1490
1491 // Add any other type-independent checks here
1492 if (readOnly_ && (context.flags & kValidateWriteFlag)) {
1493 context.error_handler.DisallowedWhenWriting();
1494 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly);
1495 }
1496 if (writeOnly_ && (context.flags & kValidateReadFlag)) {
1497 context.error_handler.DisallowedWhenReading();
1498 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly);
1499 }
1500
1501 return true;
1502 }
1503
1504 void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
1505 for (SizeType i = 0; i < schemas.count; i++)
1506 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
1507 }
1508
1509 // O(n)
1510 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1511 SizeType len = name.GetStringLength();
1512 const Ch* str = name.GetString();
1513 for (SizeType index = 0; index < propertyCount_; index++)
1514 if (properties_[index].name.GetStringLength() == len &&
1515 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1516 {
1517 *outIndex = index;
1518 return true;
1519 }
1520 return false;
1521 }
1522
1523 bool CheckBool(Context& context, bool) const {
1524 if (!(type_ & (1 << kBooleanSchemaType))) {
1525 DisallowedType(context, GetBooleanString());
1526 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1527 }
1528 return true;
1529 }
1530
1531 bool CheckInt(Context& context, int64_t i) const {
1532 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1533 DisallowedType(context, GetIntegerString());
1534 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1535 }
1536
1537 if (!minimum_.IsNull()) {
1538 if (minimum_.IsInt64()) {
1539 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
1540 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1541 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1542 }
1543 }
1544 else if (minimum_.IsUint64()) {
1545 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1546 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
1547 }
1548 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1549 return false;
1550 }
1551
1552 if (!maximum_.IsNull()) {
1553 if (maximum_.IsInt64()) {
1554 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
1555 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1556 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1557 }
1558 }
1559 else if (maximum_.IsUint64()) { }
1560 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1561 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1562 return false;
1563 }
1564
1565 if (!multipleOf_.IsNull()) {
1566 if (multipleOf_.IsUint64()) {
1567 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
1568 context.error_handler.NotMultipleOf(i, multipleOf_);
1569 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1570 }
1571 }
1572 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1573 return false;
1574 }
1575
1576 return true;
1577 }
1578
1579 bool CheckUint(Context& context, uint64_t i) const {
1580 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1581 DisallowedType(context, GetIntegerString());
1582 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1583 }
1584
1585 if (!minimum_.IsNull()) {
1586 if (minimum_.IsUint64()) {
1587 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
1588 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1589 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1590 }
1591 }
1592 else if (minimum_.IsInt64())
1593 /* do nothing */; // i >= 0 > minimum.Getint64()
1594 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1595 return false;
1596 }
1597
1598 if (!maximum_.IsNull()) {
1599 if (maximum_.IsUint64()) {
1600 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
1601 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1602 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1603 }
1604 }
1605 else if (maximum_.IsInt64()) {
1606 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1607 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
1608 }
1609 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1610 return false;
1611 }
1612
1613 if (!multipleOf_.IsNull()) {
1614 if (multipleOf_.IsUint64()) {
1615 if (i % multipleOf_.GetUint64() != 0) {
1616 context.error_handler.NotMultipleOf(i, multipleOf_);
1617 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1618 }
1619 }
1620 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1621 return false;
1622 }
1623
1624 return true;
1625 }
1626
1627 bool CheckDoubleMinimum(Context& context, double d) const {
1628 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
1629 context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
1630 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1631 }
1632 return true;
1633 }
1634
1635 bool CheckDoubleMaximum(Context& context, double d) const {
1636 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
1637 context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
1638 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1639 }
1640 return true;
1641 }
1642
1643 bool CheckDoubleMultipleOf(Context& context, double d) const {
1644 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1645 double q = a / b;
1646 double qRounded = std::floor(q + 0.5);
1647 double scaledEpsilon = (q + qRounded) * std::numeric_limits<double>::epsilon();
1648 double difference = std::abs(qRounded - q);
1649 bool isMultiple = difference <= scaledEpsilon || difference < (std::numeric_limits<double>::min)();
1650 if (!isMultiple) {
1651 context.error_handler.NotMultipleOf(d, multipleOf_);
1652 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1653 }
1654 return true;
1655 }
1656
1657 void DisallowedType(Context& context, const ValueType& actualType) const {
1658 ErrorHandler& eh = context.error_handler;
1659 eh.StartDisallowedType();
1660
1661 if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
1662 if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
1663 if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
1664 if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
1665 if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
1666
1667 if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
1668 else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
1669
1670 eh.EndDisallowedType(actualType);
1671 }
1672
1673 struct Property {
1674 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
1675 ~Property() { AllocatorType::Free(dependencies); }
1676 SValue name;
1677 const SchemaType* schema;
1678 const SchemaType* dependenciesSchema;
1679 SizeType dependenciesValidatorIndex;
1680 bool* dependencies;
1681 bool required;
1682 };
1683
1684 struct PatternProperty {
1685 PatternProperty() : schema(), pattern() {}
1686 ~PatternProperty() {
1687 if (pattern) {
1688 pattern->~RegexType();
1689 AllocatorType::Free(pattern);
1690 }
1691 }
1692 const SchemaType* schema;
1693 RegexType* pattern;
1694 };
1695
1696 AllocatorType* allocator_;
1697 SValue uri_;
1698 UriType id_;
1699 Specification spec_;
1700 PointerType pointer_;
1701 const SchemaType* typeless_;
1702 uint64_t* enum_;
1703 SizeType enumCount_;
1704 SchemaArray allOf_;
1705 SchemaArray anyOf_;
1706 SchemaArray oneOf_;
1707 const SchemaType* not_;
1708 unsigned type_; // bitmask of kSchemaType
1709 SizeType validatorCount_;
1710 SizeType notValidatorIndex_;
1711
1712 Property* properties_;
1713 const SchemaType* additionalPropertiesSchema_;
1714 PatternProperty* patternProperties_;
1715 SizeType patternPropertyCount_;
1716 SizeType propertyCount_;
1717 SizeType minProperties_;
1718 SizeType maxProperties_;
1719 bool additionalProperties_;
1720 bool hasDependencies_;
1721 bool hasRequired_;
1722 bool hasSchemaDependencies_;
1723
1724 const SchemaType* additionalItemsSchema_;
1725 const SchemaType* itemsList_;
1726 const SchemaType** itemsTuple_;
1727 SizeType itemsTupleCount_;
1728 SizeType minItems_;
1729 SizeType maxItems_;
1730 bool additionalItems_;
1731 bool uniqueItems_;
1732
1733 RegexType* pattern_;
1734 SizeType minLength_;
1735 SizeType maxLength_;
1736
1737 SValue minimum_;
1738 SValue maximum_;
1739 SValue multipleOf_;
1740 bool exclusiveMinimum_;
1741 bool exclusiveMaximum_;
1742
1743 SizeType defaultValueLength_;
1744
1745 bool readOnly_;
1746 bool writeOnly_;
1747 bool nullable_;
1748};
1749
1750template<typename Stack, typename Ch>
1752 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1753 *documentStack.template Push<Ch>() = '/';
1754 char buffer[21];
1755 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1756 for (size_t i = 0; i < length; i++)
1757 *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1758 }
1759};
1760
1761// Partial specialized version for char to prevent buffer copying.
1762template <typename Stack>
1763struct TokenHelper<Stack, char> {
1764 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1765 RAPIDJSON_IF_CONSTEXPR (sizeof(SizeType) == 4) {
1766 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1767 *buffer++ = '/';
1768 const char* end = internal::u32toa(index, buffer);
1769 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1770 }
1771 else {
1772 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1773 *buffer++ = '/';
1774 const char* end = internal::u64toa(index, buffer);
1775 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1776 }
1777 }
1778};
1779
1780} // namespace internal
1781
1783// IGenericRemoteSchemaDocumentProvider
1784
1785template <typename SchemaDocumentType>
1787public:
1788 typedef typename SchemaDocumentType::Ch Ch;
1789 typedef typename SchemaDocumentType::ValueType ValueType;
1790 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
1791
1793 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1794 virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri<ValueType, AllocatorType> uri, Specification& spec) {
1795 // Default implementation just calls through for compatibility
1796 // Following line suppresses unused parameter warning
1797 (void)spec;
1798 // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi);
1799 return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength());
1800 }
1801};
1802
1804// GenericSchemaDocument
1805
1807
1815template <typename ValueT, typename Allocator = CrtAllocator>
1817public:
1818 typedef ValueT ValueType;
1819 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1820 typedef Allocator AllocatorType;
1821 typedef typename ValueType::EncodingType EncodingType;
1822 typedef typename EncodingType::Ch Ch;
1823 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1824 typedef GenericPointer<ValueType, Allocator> PointerType;
1826 typedef GenericUri<ValueType, Allocator> UriType;
1827 typedef GenericStringRef<Ch> StringRefType;
1829 template <typename, typename, typename>
1830 friend class GenericSchemaValidator;
1831
1833
1844 explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
1845 IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
1846 const PointerType& pointer = PointerType(), // PR #1393
1847 const Specification& spec = Specification(kDraft04)) :
1848 remoteProvider_(remoteProvider),
1849 allocator_(allocator),
1850 ownAllocator_(),
1851 root_(),
1852 typeless_(),
1853 schemaMap_(allocator, kInitialSchemaMapSize),
1854 schemaRef_(allocator, kInitialSchemaRefSize),
1855 spec_(spec),
1856 error_(kObjectType),
1857 currentError_()
1858 {
1859 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument");
1860 if (!allocator_)
1861 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1862
1863 Ch noUri[1] = {0};
1864 uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
1865 docId_ = UriType(uri_, allocator_);
1866
1867 typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1868 new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
1869
1870 // Establish the schema draft or open api version.
1871 // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document.
1872 SetSchemaSpecification(document);
1873
1874 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1875 // And call HandleRefSchema() if there are $ref.
1876 // PR #1393 use input pointer if supplied
1877 root_ = typeless_;
1878 if (pointer.GetTokenCount() == 0) {
1879 CreateSchemaRecursive(&root_, pointer, document, document, docId_);
1880 }
1881 else if (const ValueType* v = pointer.Get(document)) {
1882 CreateSchema(&root_, pointer, *v, document, docId_);
1883 }
1884 else {
1886 pointer.StringifyUriFragment(sb);
1887 SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)));
1888 }
1889
1890 RAPIDJSON_ASSERT(root_ != 0);
1891
1892 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1893 }
1894
1895#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1897 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1898 remoteProvider_(rhs.remoteProvider_),
1899 allocator_(rhs.allocator_),
1900 ownAllocator_(rhs.ownAllocator_),
1901 root_(rhs.root_),
1902 typeless_(rhs.typeless_),
1903 schemaMap_(std::move(rhs.schemaMap_)),
1904 schemaRef_(std::move(rhs.schemaRef_)),
1905 uri_(std::move(rhs.uri_)),
1906 docId_(std::move(rhs.docId_)),
1907 spec_(rhs.spec_),
1908 error_(std::move(rhs.error_)),
1909 currentError_(std::move(rhs.currentError_))
1910 {
1911 rhs.remoteProvider_ = 0;
1912 rhs.allocator_ = 0;
1913 rhs.ownAllocator_ = 0;
1914 rhs.typeless_ = 0;
1915 }
1916#endif
1917
1920 while (!schemaMap_.Empty())
1921 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1922
1923 if (typeless_) {
1924 typeless_->~SchemaType();
1925 Allocator::Free(typeless_);
1926 }
1927
1928 // these may contain some allocator data so clear before deleting ownAllocator_
1929 uri_.SetNull();
1930 error_.SetNull();
1931 currentError_.SetNull();
1932
1933 RAPIDJSON_DELETE(ownAllocator_);
1934 }
1935
1936 const GValue& GetURI() const { return uri_; }
1937
1938 const Specification& GetSpecification() const { return spec_; }
1939 bool IsSupportedSpecification() const { return spec_.IsSupported(); }
1940
1942 // Returns kDraftNone if document is silent
1943 static const Specification GetSpecification(const ValueType& document) {
1944 SchemaDraft draft = GetSchemaDraft(document);
1945 if (draft != kDraftNone)
1946 return Specification(draft);
1947 else {
1948 OpenApiVersion oapi = GetOpenApiVersion(document);
1949 if (oapi != kVersionNone)
1950 return Specification(oapi);
1951 }
1952 return Specification(kDraftNone);
1953 }
1954
1956 const SchemaType& GetRoot() const { return *root_; }
1957
1959 GValue& GetError() { return error_; }
1960 const GValue& GetError() const { return error_; }
1961
1962 static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) {
1963 switch (schemaErrorCode) {
1964 case kSchemaErrorStartUnknown: return GetStartUnknownString();
1965 case kSchemaErrorRefPlainName: return GetRefPlainNameString();
1966 case kSchemaErrorRefInvalid: return GetRefInvalidString();
1967 case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString();
1968 case kSchemaErrorRefUnknown: return GetRefUnknownString();
1969 case kSchemaErrorRefCyclical: return GetRefCyclicalString();
1970 case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString();
1971 case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString();
1972 case kSchemaErrorRegexInvalid: return GetRegexInvalidString();
1973 case kSchemaErrorSpecUnknown: return GetSpecUnknownString();
1974 case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString();
1975 case kSchemaErrorSpecIllegal: return GetSpecIllegalString();
1976 case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString();
1977 default: return GetNullString();
1978 }
1979 }
1980
1982 void SchemaError(const SchemaErrorCode code, const PointerType& location) {
1983 currentError_ = GValue(kObjectType);
1984 AddCurrentError(code, location);
1985 }
1986
1988 void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) {
1989 currentError_ = GValue(kObjectType);
1990 currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
1991 AddCurrentError(code, location);
1992 }
1993
1995 void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) {
1996 currentError_ = GValue(kObjectType);
1997 currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
1998 currentError_.AddMember(GetOffsetString(), static_cast<SizeType>(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_);
1999 AddCurrentError(code, location);
2000 }
2001
2002 private:
2007
2008 typedef const PointerType* SchemaRefPtr; // PR #1393
2009
2010 struct SchemaEntry {
2011 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
2012 ~SchemaEntry() {
2013 if (owned) {
2014 schema->~SchemaType();
2015 Allocator::Free(schema);
2016 }
2017 }
2018 PointerType pointer;
2019 SchemaType* schema;
2020 bool owned;
2021 };
2022
2023 void AddErrorInstanceLocation(GValue& result, const PointerType& location) {
2025 location.StringifyUriFragment(sb);
2026 GValue instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)), *allocator_);
2027 result.AddMember(GetInstanceRefString(), instanceRef, *allocator_);
2028 }
2029
2030 void AddError(GValue& keyword, GValue& error) {
2031 typename GValue::MemberIterator member = error_.FindMember(keyword);
2032 if (member == error_.MemberEnd())
2033 error_.AddMember(keyword, error, *allocator_);
2034 else {
2035 if (member->value.IsObject()) {
2036 GValue errors(kArrayType);
2037 errors.PushBack(member->value, *allocator_);
2038 member->value = errors;
2039 }
2040 member->value.PushBack(error, *allocator_);
2041 }
2042 }
2043
2044 void AddCurrentError(const SchemaErrorCode code, const PointerType& location) {
2045 RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code));
2046 currentError_.AddMember(GetErrorCodeString(), code, *allocator_);
2047 AddErrorInstanceLocation(currentError_, location);
2048 AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_);
2049 }
2050
2051#define RAPIDJSON_STRING_(name, ...) \
2052 static const StringRefType& Get##name##String() {\
2053 static const Ch s[] = { __VA_ARGS__, '\0' };\
2054 static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
2055 return v;\
2056 }
2057
2058 RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
2059 RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
2060 RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e')
2061 RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't')
2062
2063 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
2064 RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
2065 RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd')
2066 RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l')
2067 RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
2068 RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e')
2069 RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
2070 RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
2071 RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
2072 RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l')
2073 RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r')
2074 RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a')
2075 RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
2076 RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
2077
2078#undef RAPIDJSON_STRING_
2079
2080 // Static method to get schema draft of any schema document
2081 static SchemaDraft GetSchemaDraft(const ValueType& document) {
2082 static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
2083 static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
2084 static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
2085 static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
2086 static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
2087 static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
2088 static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
2089
2090 if (!document.IsObject()) {
2091 return kDraftNone;
2092 }
2093
2094 // Get the schema draft from the $schema keyword at the supplied location
2095 typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString());
2096 if (itr != document.MemberEnd()) {
2097 if (!itr->value.IsString()) return kDraftUnknown;
2098 const UriType draftUri(itr->value);
2099 // Check base uri for match
2100 if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04;
2101 if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05;
2102 if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06;
2103 if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07;
2104 if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03;
2105 if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09;
2106 if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12;
2107 return kDraftUnknown;
2108 }
2109 // $schema not found
2110 return kDraftNone;
2111 }
2112
2113
2114 // Get open api version of any schema document
2115 static OpenApiVersion GetOpenApiVersion(const ValueType& document) {
2116 static const Ch kVersion20String[] = { '2', '.', '0', '\0' };
2117 static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level
2118 static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level
2119 static SizeType len = internal::StrLen<Ch>(kVersion30String);
2120
2121 if (!document.IsObject()) {
2122 return kVersionNone;
2123 }
2124
2125 // Get the open api version from the swagger / openapi keyword at the supplied location
2126 typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString());
2127 if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString());
2128 if (itr != document.MemberEnd()) {
2129 if (!itr->value.IsString()) return kVersionUnknown;
2130 const ValueType kVersion20Value(kVersion20String);
2131 if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly
2132 const ValueType kVersion30Value(kVersion30String);
2133 if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x
2134 const ValueType kVersion31Value(kVersion31String);
2135 if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x
2136 return kVersionUnknown;
2137 }
2138 // swagger or openapi not found
2139 return kVersionNone;
2140 }
2141
2142 // Get the draft of the schema or the open api version (which implies the draft).
2143 // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on.
2144 void SetSchemaSpecification(const ValueType& document) {
2145 // Look for '$schema', 'swagger' or 'openapi' keyword at document root
2146 SchemaDraft docDraft = GetSchemaDraft(document);
2147 OpenApiVersion docOapi = GetOpenApiVersion(document);
2148 // Error if both in document
2149 if (docDraft != kDraftNone && docOapi != kVersionNone)
2150 SchemaError(kSchemaErrorSpecIllegal, PointerType());
2151 // Use document draft or open api version if present or use spec from constructor
2152 if (docDraft != kDraftNone)
2153 spec_ = Specification(docDraft);
2154 else if (docOapi != kVersionNone)
2155 spec_ = Specification(docOapi);
2156 // Error if draft or version unknown
2157 if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown)
2158 SchemaError(kSchemaErrorSpecUnknown, PointerType());
2159 else if (!spec_.IsSupported())
2161 }
2162
2163 // Changed by PR #1393
2164 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
2165 if (v.GetType() == kObjectType) {
2166 UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_);
2167
2168 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
2169 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
2170 }
2171 else if (v.GetType() == kArrayType)
2172 for (SizeType i = 0; i < v.Size(); i++)
2173 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id);
2174 }
2175
2176 // Changed by PR #1393
2177 const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
2178 RAPIDJSON_ASSERT(pointer.IsValid());
2179 GenericStringBuffer<EncodingType> sb;
2180 pointer.StringifyUriFragment(sb);
2181 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString());
2182 if (v.IsObject()) {
2183 if (const SchemaType* sc = GetSchema(pointer)) {
2184 if (schema)
2185 *schema = sc;
2186 AddSchemaRefs(const_cast<SchemaType*>(sc));
2187 }
2188 else if (!HandleRefSchema(pointer, schema, v, document, id)) {
2189 // The new schema constructor adds itself and its $ref(s) to schemaMap_
2190 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
2191 if (schema)
2192 *schema = s;
2193 return s->GetId();
2194 }
2195 }
2196 else {
2197 if (schema)
2198 *schema = typeless_;
2199 AddSchemaRefs(typeless_);
2200 }
2201 return id;
2202 }
2203
2204 // Changed by PR #1393
2205 // TODO should this return a UriType& ?
2206 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
2207 typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
2208 if (itr == v.MemberEnd())
2209 return false;
2210
2211 GenericStringBuffer<EncodingType> sb;
2212 source.StringifyUriFragment(sb);
2213 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString());
2214 // Resolve the source pointer to the $ref'ed schema (finally)
2215 new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
2216
2217 if (itr->value.IsString()) {
2218 SizeType len = itr->value.GetStringLength();
2219 if (len == 0)
2221 else {
2222 // First resolve $ref against the in-scope id
2223 UriType scopeId = UriType(id, allocator_);
2224 UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
2225 RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString());
2226 // See if the resolved $ref minus the fragment matches a resolved id in this document
2227 // Search from the root. Returns the subschema in the document and its absolute JSON pointer.
2228 PointerType basePointer = PointerType();
2229 const ValueType *base = FindId(document, ref, basePointer, docId_, false);
2230 if (!base) {
2231 // Remote reference - call the remote document provider
2232 if (!remoteProvider_)
2234 else {
2235 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) {
2236 const Ch* s = ref.GetFragString();
2237 len = ref.GetFragStringLength();
2238 if (len <= 1 || s[1] == '/') {
2239 // JSON pointer fragment, absolute in the remote schema
2240 const PointerType pointer(s, len, allocator_);
2241 if (!pointer.IsValid())
2242 SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer);
2243 else {
2244 // Get the subschema
2245 if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
2246 if (schema)
2247 *schema = sc;
2248 AddSchemaRefs(const_cast<SchemaType *>(sc));
2249 return true;
2250 } else
2251 SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
2252 }
2253 } else
2254 // Plain name fragment, not allowed in remote schema
2256 } else
2257 SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength());
2258 }
2259 }
2260 else { // Local reference
2261 const Ch* s = ref.GetFragString();
2262 len = ref.GetFragStringLength();
2263 if (len <= 1 || s[1] == '/') {
2264 // JSON pointer fragment, relative to the resolved URI
2265 const PointerType relPointer(s, len, allocator_);
2266 if (!relPointer.IsValid())
2267 SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer);
2268 else {
2269 // Get the subschema
2270 if (const ValueType *pv = relPointer.Get(*base)) {
2271 // Now get the absolute JSON pointer by adding relative to base
2272 PointerType pointer(basePointer, allocator_);
2273 for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
2274 pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
2275 if (IsCyclicRef(pointer))
2276 SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
2277 else {
2278 // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
2279 // TODO: cache pointer <-> id mapping
2280 size_t unresolvedTokenIndex;
2281 scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
2282 CreateSchema(schema, pointer, *pv, document, scopeId);
2283 return true;
2284 }
2285 } else
2286 SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
2287 }
2288 } else {
2289 // Plain name fragment, relative to the resolved URI
2290 // Not supported in open api 2.0 and 3.0
2291 PointerType pointer(allocator_);
2292 if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30)
2294 // See if the fragment matches an id in this document.
2295 // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
2296 else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
2297 if (IsCyclicRef(pointer))
2298 SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
2299 else {
2300 // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
2301 // TODO: cache pointer <-> id mapping
2302 size_t unresolvedTokenIndex;
2303 scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
2304 CreateSchema(schema, pointer, *pv, document, scopeId);
2305 return true;
2306 }
2307 } else
2308 SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
2309 }
2310 }
2311 }
2312 }
2313
2314 // Invalid/Unknown $ref
2315 if (schema)
2316 *schema = typeless_;
2317 AddSchemaRefs(typeless_);
2318 return true;
2319 }
2320
2322 // If full specified use all URI else ignore fragment.
2323 // If found, return a pointer to the subschema and its JSON pointer.
2324 // TODO cache pointer <-> id mapping
2325 ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const {
2326 SizeType i = 0;
2327 ValueType* resval = 0;
2328 UriType tempuri = UriType(finduri, allocator_);
2329 UriType localuri = UriType(baseuri, allocator_);
2330 if (doc.GetType() == kObjectType) {
2331 // Establish the base URI of this object
2332 typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
2333 if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
2334 localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
2335 }
2336 // See if it matches
2337 if (localuri.Match(finduri, full)) {
2338 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString());
2339 resval = const_cast<ValueType *>(&doc);
2340 resptr = here;
2341 return resval;
2342 }
2343 // No match, continue looking
2344 for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
2345 if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
2346 resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
2347 }
2348 if (resval) break;
2349 }
2350 } else if (doc.GetType() == kArrayType) {
2351 // Continue looking
2352 for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
2353 if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
2354 resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
2355 }
2356 if (resval) break;
2357 i++;
2358 }
2359 }
2360 return resval;
2361 }
2362
2363 // Added by PR #1393
2364 void AddSchemaRefs(SchemaType* schema) {
2365 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs");
2366 while (!schemaRef_.Empty()) {
2367 SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
2368 SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
2369 new (entry) SchemaEntry(**ref, schema, false, allocator_);
2370 }
2371 }
2372
2373 // Added by PR #1393
2374 bool IsCyclicRef(const PointerType& pointer) const {
2375 for (const SchemaRefPtr* ref = schemaRef_.template Bottom<SchemaRefPtr>(); ref != schemaRef_.template End<SchemaRefPtr>(); ++ref)
2376 if (pointer == **ref)
2377 return true;
2378 return false;
2379 }
2380
2381 const SchemaType* GetSchema(const PointerType& pointer) const {
2382 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
2383 if (pointer == target->pointer)
2384 return target->schema;
2385 return 0;
2386 }
2387
2388 PointerType GetPointer(const SchemaType* schema) const {
2389 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
2390 if (schema == target->schema)
2391 return target->pointer;
2392 return PointerType();
2393 }
2394
2395 const SchemaType* GetTypeless() const { return typeless_; }
2396
2397 static const size_t kInitialSchemaMapSize = 64;
2398 static const size_t kInitialSchemaRefSize = 64;
2399
2400 IRemoteSchemaDocumentProviderType* remoteProvider_;
2401 Allocator *allocator_;
2402 Allocator *ownAllocator_;
2403 const SchemaType* root_;
2404 SchemaType* typeless_;
2405 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
2406 internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
2407 GValue uri_; // Schema document URI
2408 UriType docId_;
2409 Specification spec_;
2410 GValue error_;
2411 GValue currentError_;
2412};
2413
2415typedef GenericSchemaDocument<Value> SchemaDocument;
2417typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
2418
2420// GenericSchemaValidator
2421
2423
2434template <
2435 typename SchemaDocumentType,
2437 typename StateAllocator = CrtAllocator>
2439 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
2441 public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
2442public:
2443 typedef typename SchemaDocumentType::SchemaType SchemaType;
2444 typedef typename SchemaDocumentType::PointerType PointerType;
2445 typedef typename SchemaType::EncodingType EncodingType;
2446 typedef typename SchemaType::SValue SValue;
2447 typedef typename EncodingType::Ch Ch;
2448 typedef GenericStringRef<Ch> StringRefType;
2450
2452
2459 const SchemaDocumentType& schemaDocument,
2460 StateAllocator* allocator = 0,
2461 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2462 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2463 :
2464 schemaDocument_(&schemaDocument),
2465 root_(schemaDocument.GetRoot()),
2466 stateAllocator_(allocator),
2467 ownStateAllocator_(0),
2468 schemaStack_(allocator, schemaStackCapacity),
2469 documentStack_(allocator, documentStackCapacity),
2470 outputHandler_(0),
2471 error_(kObjectType),
2472 currentError_(),
2473 missingDependents_(),
2474 valid_(true),
2475 flags_(kValidateDefaultFlags),
2476 depth_(0)
2477 {
2478 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator");
2479 }
2480
2482
2489 const SchemaDocumentType& schemaDocument,
2490 OutputHandler& outputHandler,
2491 StateAllocator* allocator = 0,
2492 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2493 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2494 :
2495 schemaDocument_(&schemaDocument),
2496 root_(schemaDocument.GetRoot()),
2497 stateAllocator_(allocator),
2498 ownStateAllocator_(0),
2499 schemaStack_(allocator, schemaStackCapacity),
2500 documentStack_(allocator, documentStackCapacity),
2501 outputHandler_(&outputHandler),
2502 error_(kObjectType),
2503 currentError_(),
2504 missingDependents_(),
2505 valid_(true),
2506 flags_(kValidateDefaultFlags),
2507 depth_(0)
2508 {
2509 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)");
2510 }
2511
2514 Reset();
2515 RAPIDJSON_DELETE(ownStateAllocator_);
2516 }
2517
2519 void Reset() {
2520 while (!schemaStack_.Empty())
2521 PopSchema();
2522 documentStack_.Clear();
2523 ResetError();
2524 }
2525
2527 void ResetError() {
2528 error_.SetObject();
2529 currentError_.SetNull();
2530 missingDependents_.SetNull();
2531 valid_ = true;
2532 }
2533
2535 void SetValidateFlags(unsigned flags) {
2536 flags_ = flags;
2537 }
2538 virtual unsigned GetValidateFlags() const {
2539 return flags_;
2540 }
2541
2542 virtual bool IsValid() const {
2543 if (!valid_) return false;
2544 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
2545 return true;
2546 }
2548
2550 ValueType& GetError() { return error_; }
2551 const ValueType& GetError() const { return error_; }
2552
2554 // If reporting all errors, the stack will be empty.
2555 PointerType GetInvalidSchemaPointer() const {
2556 return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
2557 }
2558
2560 // If reporting all errors, the stack will be empty, so return "errors".
2561 const Ch* GetInvalidSchemaKeyword() const {
2562 if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
2563 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return static_cast<const Ch*>(GetErrorsString());
2564 return 0;
2565 }
2566
2568 // If reporting all errors, the stack will be empty, so return kValidateErrors.
2570 if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
2571 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
2572 return kValidateErrorNone;
2573 }
2574
2576 // If reporting all errors, the stack will be empty.
2577 PointerType GetInvalidDocumentPointer() const {
2578 if (documentStack_.Empty()) {
2579 return PointerType();
2580 }
2581 else {
2582 return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
2583 }
2584 }
2585
2586 void NotMultipleOf(int64_t actual, const SValue& expected) {
2587 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
2588 }
2589 void NotMultipleOf(uint64_t actual, const SValue& expected) {
2590 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
2591 }
2592 void NotMultipleOf(double actual, const SValue& expected) {
2593 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
2594 }
2595 void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
2596 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2597 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2598 }
2599 void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
2600 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2601 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2602 }
2603 void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
2604 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2605 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2606 }
2607 void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
2608 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2609 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2610 }
2611 void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
2612 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2613 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2614 }
2615 void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
2616 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2617 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2618 }
2619
2620 void TooLong(const Ch* str, SizeType length, SizeType expected) {
2621 AddNumberError(kValidateErrorMaxLength,
2622 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
2623 }
2624 void TooShort(const Ch* str, SizeType length, SizeType expected) {
2625 AddNumberError(kValidateErrorMinLength,
2626 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
2627 }
2628 void DoesNotMatch(const Ch* str, SizeType length) {
2629 currentError_.SetObject();
2630 currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
2631 AddCurrentError(kValidateErrorPattern);
2632 }
2633
2634 void DisallowedItem(SizeType index) {
2635 currentError_.SetObject();
2636 currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
2637 AddCurrentError(kValidateErrorAdditionalItems, true);
2638 }
2639 void TooFewItems(SizeType actualCount, SizeType expectedCount) {
2640 AddNumberError(kValidateErrorMinItems,
2641 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2642 }
2643 void TooManyItems(SizeType actualCount, SizeType expectedCount) {
2644 AddNumberError(kValidateErrorMaxItems,
2645 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2646 }
2647 void DuplicateItems(SizeType index1, SizeType index2) {
2648 ValueType duplicates(kArrayType);
2649 duplicates.PushBack(index1, GetStateAllocator());
2650 duplicates.PushBack(index2, GetStateAllocator());
2651 currentError_.SetObject();
2652 currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
2653 AddCurrentError(kValidateErrorUniqueItems, true);
2654 }
2655
2656 void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
2657 AddNumberError(kValidateErrorMaxProperties,
2658 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2659 }
2660 void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
2661 AddNumberError(kValidateErrorMinProperties,
2662 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2663 }
2664 void StartMissingProperties() {
2665 currentError_.SetArray();
2666 }
2667 void AddMissingProperty(const SValue& name) {
2668 currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
2669 }
2670 bool EndMissingProperties() {
2671 if (currentError_.Empty())
2672 return false;
2673 ValueType error(kObjectType);
2674 error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
2675 currentError_ = error;
2676 AddCurrentError(kValidateErrorRequired);
2677 return true;
2678 }
2679 void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
2680 for (SizeType i = 0; i < count; ++i)
2681 MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2682 }
2683 void DisallowedProperty(const Ch* name, SizeType length) {
2684 currentError_.SetObject();
2685 currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
2686 AddCurrentError(kValidateErrorAdditionalProperties, true);
2687 }
2688
2689 void StartDependencyErrors() {
2690 currentError_.SetObject();
2691 }
2692 void StartMissingDependentProperties() {
2693 missingDependents_.SetArray();
2694 }
2695 void AddMissingDependentProperty(const SValue& targetName) {
2696 missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
2697 }
2698 void EndMissingDependentProperties(const SValue& sourceName) {
2699 if (!missingDependents_.Empty()) {
2700 // Create equivalent 'required' error
2701 ValueType error(kObjectType);
2703 error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
2704 AddErrorCode(error, code);
2705 AddErrorInstanceLocation(error, false);
2706 // When appending to a pointer ensure its allocator is used
2707 PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
2708 AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
2709 ValueType wrapper(kObjectType);
2710 wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
2711 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
2712 }
2713 }
2714 void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
2715 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
2716 static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
2717 }
2718 bool EndDependencyErrors() {
2719 if (currentError_.ObjectEmpty())
2720 return false;
2721 ValueType error(kObjectType);
2722 error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
2723 currentError_ = error;
2724 AddCurrentError(kValidateErrorDependencies);
2725 return true;
2726 }
2727
2728 void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
2729 currentError_.SetObject();
2730 AddCurrentError(code);
2731 }
2732 void StartDisallowedType() {
2733 currentError_.SetArray();
2734 }
2735 void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
2736 currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
2737 }
2738 void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
2739 ValueType error(kObjectType);
2740 error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
2741 error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
2742 currentError_ = error;
2743 AddCurrentError(kValidateErrorType);
2744 }
2745 void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
2746 // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
2747 AddErrorArray(kValidateErrorAllOf, subvalidators, count);
2748 //for (SizeType i = 0; i < count; ++i) {
2749 // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2750 //}
2751 }
2752 void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
2753 AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
2754 }
2755 void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
2756 AddErrorArray(kValidateErrorOneOf, subvalidators, count);
2757 }
2758 void MultipleOneOf(SizeType index1, SizeType index2) {
2759 ValueType matches(kArrayType);
2760 matches.PushBack(index1, GetStateAllocator());
2761 matches.PushBack(index2, GetStateAllocator());
2762 currentError_.SetObject();
2763 currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator());
2764 AddCurrentError(kValidateErrorOneOfMatch);
2765 }
2766 void Disallowed() {
2767 currentError_.SetObject();
2768 AddCurrentError(kValidateErrorNot);
2769 }
2770 void DisallowedWhenWriting() {
2771 currentError_.SetObject();
2772 AddCurrentError(kValidateErrorReadOnly);
2773 }
2774 void DisallowedWhenReading() {
2775 currentError_.SetObject();
2776 AddCurrentError(kValidateErrorWriteOnly);
2777 }
2778
2779#define RAPIDJSON_STRING_(name, ...) \
2780 static const StringRefType& Get##name##String() {\
2781 static const Ch s[] = { __VA_ARGS__, '\0' };\
2782 static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
2783 return v;\
2784 }
2785
2786 RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
2787 RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
2788 RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
2789 RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
2790 RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
2791 RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
2792 RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
2793 RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
2794 RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
2795 RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
2796 RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's')
2797
2798#undef RAPIDJSON_STRING_
2799
2800#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
2801 if (!valid_) return false; \
2802 if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
2803 *documentStack_.template Push<Ch>() = '\0';\
2804 documentStack_.template Pop<Ch>(1);\
2805 RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom<Ch>());\
2806 valid_ = false;\
2807 return valid_;\
2808 }
2809
2810#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
2811 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
2812 if (context->hasher)\
2813 static_cast<HasherType*>(context->hasher)->method arg2;\
2814 if (context->validators)\
2815 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
2816 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
2817 if (context->patternPropertiesValidators)\
2818 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
2819 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
2820 }
2821
2822#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
2823 valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
2824 return valid_;
2825
2826#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
2827 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
2828 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
2829 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
2830
2831 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); }
2832 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
2833 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
2834 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
2835 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
2836 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
2837 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
2838 bool RawNumber(const Ch* str, SizeType length, bool copy)
2839 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2840 bool String(const Ch* str, SizeType length, bool copy)
2841 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2842
2843 bool StartObject() {
2844 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject");
2845 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
2846 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
2847 valid_ = !outputHandler_ || outputHandler_->StartObject();
2848 return valid_;
2849 }
2850
2851 bool Key(const Ch* str, SizeType len, bool copy) {
2852 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str);
2853 if (!valid_) return false;
2854 AppendToken(str, len);
2855 if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) {
2856 valid_ = false;
2857 return valid_;
2858 }
2859 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
2860 valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
2861 return valid_;
2862 }
2863
2864 bool EndObject(SizeType memberCount) {
2865 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject");
2866 if (!valid_) return false;
2867 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
2868 if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) {
2869 valid_ = false;
2870 return valid_;
2871 }
2872 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
2873 }
2874
2875 bool StartArray() {
2876 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray");
2877 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
2878 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
2879 valid_ = !outputHandler_ || outputHandler_->StartArray();
2880 return valid_;
2881 }
2882
2883 bool EndArray(SizeType elementCount) {
2884 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray");
2885 if (!valid_) return false;
2886 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
2887 if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) {
2888 valid_ = false;
2889 return valid_;
2890 }
2891 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
2892 }
2893
2894#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
2895#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
2896#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
2897
2898 // Implementation of ISchemaStateFactory<SchemaType>
2899 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
2900 *documentStack_.template Push<Ch>() = '\0';
2901 documentStack_.template Pop<Ch>(1);
2902 ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
2903 depth_ + 1,
2904 &GetStateAllocator());
2905 sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~static_cast<unsigned>(kValidateContinueOnErrorFlag));
2906 return sv;
2907 }
2908
2909 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
2910 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
2912 StateAllocator::Free(v);
2913 }
2914
2915 virtual void* CreateHasher() {
2916 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
2917 }
2918
2919 virtual uint64_t GetHashCode(void* hasher) {
2920 return static_cast<HasherType*>(hasher)->GetHashCode();
2921 }
2922
2923 virtual void DestroryHasher(void* hasher) {
2924 HasherType* h = static_cast<HasherType*>(hasher);
2925 h->~HasherType();
2926 StateAllocator::Free(h);
2927 }
2928
2929 virtual void* MallocState(size_t size) {
2930 return GetStateAllocator().Malloc(size);
2931 }
2932
2933 virtual void FreeState(void* p) {
2934 StateAllocator::Free(p);
2935 }
2936 // End of implementation of ISchemaStateFactory<SchemaType>
2937
2938private:
2939 typedef typename SchemaType::Context Context;
2940 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
2941 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
2942
2944 const SchemaDocumentType& schemaDocument,
2945 const SchemaType& root,
2946 const char* basePath, size_t basePathSize,
2947 unsigned depth,
2948 StateAllocator* allocator = 0,
2949 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2950 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2951 :
2952 schemaDocument_(&schemaDocument),
2953 root_(root),
2954 stateAllocator_(allocator),
2955 ownStateAllocator_(0),
2956 schemaStack_(allocator, schemaStackCapacity),
2957 documentStack_(allocator, documentStackCapacity),
2958 outputHandler_(0),
2959 error_(kObjectType),
2960 currentError_(),
2961 missingDependents_(),
2962 valid_(true),
2963 flags_(kValidateDefaultFlags),
2964 depth_(depth)
2965 {
2966 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : "");
2967 if (basePath && basePathSize)
2968 memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
2969 }
2970
2971 StateAllocator& GetStateAllocator() {
2972 if (!stateAllocator_)
2973 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
2974 return *stateAllocator_;
2975 }
2976
2977 bool GetContinueOnErrors() const {
2978 return flags_ & kValidateContinueOnErrorFlag;
2979 }
2980
2981 bool BeginValue() {
2982 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue");
2983 if (schemaStack_.Empty())
2984 PushSchema(root_);
2985 else {
2986 if (CurrentContext().inArray)
2987 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
2988
2989 if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
2990 return false;
2991
2992 SizeType count = CurrentContext().patternPropertiesSchemaCount;
2993 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
2994 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
2995 bool valueUniqueness = CurrentContext().valueUniqueness;
2996 RAPIDJSON_ASSERT(CurrentContext().valueSchema);
2997 PushSchema(*CurrentContext().valueSchema);
2998
2999 if (count > 0) {
3000 CurrentContext().objectPatternValidatorType = patternValidatorType;
3001 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
3002 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
3003 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
3004 std::memset(va, 0, sizeof(ISchemaValidator*) * count);
3005 for (SizeType i = 0; i < count; i++)
3006 va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
3007 }
3008
3009 CurrentContext().arrayUniqueness = valueUniqueness;
3010 }
3011 return true;
3012 }
3013
3014 bool EndValue() {
3015 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue");
3016 if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
3017 return false;
3018
3019 GenericStringBuffer<EncodingType> sb;
3020 schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb);
3021 *documentStack_.template Push<Ch>() = '\0';
3022 documentStack_.template Pop<Ch>(1);
3023 RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom<Ch>(), depth_);
3024 void* hasher = CurrentContext().hasher;
3025 uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
3026
3027 PopSchema();
3028
3029 if (!schemaStack_.Empty()) {
3030 Context& context = CurrentContext();
3031 // Only check uniqueness if there is a hasher
3032 if (hasher && context.valueUniqueness) {
3033 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
3034 if (!a)
3035 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
3036 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
3037 if (itr->GetUint64() == h) {
3038 DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
3039 // Cleanup before returning if continuing
3040 if (GetContinueOnErrors()) {
3041 a->PushBack(h, GetStateAllocator());
3042 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
3043 }
3044 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
3045 }
3046 a->PushBack(h, GetStateAllocator());
3047 }
3048 }
3049
3050 // Remove the last token of document pointer
3051 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
3052 ;
3053
3054 return true;
3055 }
3056
3057 void AppendToken(const Ch* str, SizeType len) {
3058 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
3059 *documentStack_.template PushUnsafe<Ch>() = '/';
3060 for (SizeType i = 0; i < len; i++) {
3061 if (str[i] == '~') {
3062 *documentStack_.template PushUnsafe<Ch>() = '~';
3063 *documentStack_.template PushUnsafe<Ch>() = '0';
3064 }
3065 else if (str[i] == '/') {
3066 *documentStack_.template PushUnsafe<Ch>() = '~';
3067 *documentStack_.template PushUnsafe<Ch>() = '1';
3068 }
3069 else
3070 *documentStack_.template PushUnsafe<Ch>() = str[i];
3071 }
3072 }
3073
3074 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema, flags_); }
3075
3076 RAPIDJSON_FORCEINLINE void PopSchema() {
3077 Context* c = schemaStack_.template Pop<Context>(1);
3078 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
3079 a->~HashCodeArray();
3080 StateAllocator::Free(a);
3081 }
3082 c->~Context();
3083 }
3084
3085 void AddErrorInstanceLocation(ValueType& result, bool parent) {
3086 GenericStringBuffer<EncodingType> sb;
3087 PointerType instancePointer = GetInvalidDocumentPointer();
3088 ((parent && instancePointer.GetTokenCount() > 0)
3089 ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
3090 : instancePointer).StringifyUriFragment(sb);
3091 ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
3092 GetStateAllocator());
3093 result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
3094 }
3095
3096 void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
3097 GenericStringBuffer<EncodingType> sb;
3098 SizeType len = CurrentSchema().GetURI().GetStringLength();
3099 if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
3100 if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
3101 else GetInvalidSchemaPointer().StringifyUriFragment(sb);
3102 ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
3103 GetStateAllocator());
3104 result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
3105 }
3106
3107 void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
3108 result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
3109 }
3110
3111 void AddError(ValueType& keyword, ValueType& error) {
3112 typename ValueType::MemberIterator member = error_.FindMember(keyword);
3113 if (member == error_.MemberEnd())
3114 error_.AddMember(keyword, error, GetStateAllocator());
3115 else {
3116 if (member->value.IsObject()) {
3117 ValueType errors(kArrayType);
3118 errors.PushBack(member->value, GetStateAllocator());
3119 member->value = errors;
3120 }
3121 member->value.PushBack(error, GetStateAllocator());
3122 }
3123 }
3124
3125 void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
3126 AddErrorCode(currentError_, code);
3127 AddErrorInstanceLocation(currentError_, parent);
3128 AddErrorSchemaLocation(currentError_);
3129 AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
3130 }
3131
3132 void MergeError(ValueType& other) {
3133 for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
3134 AddError(it->name, it->value);
3135 }
3136 }
3137
3138 void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
3139 const typename SchemaType::ValueType& (*exclusive)() = 0) {
3140 currentError_.SetObject();
3141 currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
3142 currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
3143 if (exclusive)
3144 currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
3145 AddCurrentError(code);
3146 }
3147
3148 void AddErrorArray(const ValidateErrorCode code,
3149 ISchemaValidator** subvalidators, SizeType count) {
3150 ValueType errors(kArrayType);
3151 for (SizeType i = 0; i < count; ++i)
3152 errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
3153 currentError_.SetObject();
3154 currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
3155 AddCurrentError(code);
3156 }
3157
3158 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
3159 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
3160 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
3161
3162 static const size_t kDefaultSchemaStackCapacity = 1024;
3163 static const size_t kDefaultDocumentStackCapacity = 256;
3164 const SchemaDocumentType* schemaDocument_;
3165 const SchemaType& root_;
3166 StateAllocator* stateAllocator_;
3167 StateAllocator* ownStateAllocator_;
3170 OutputHandler* outputHandler_;
3171 ValueType error_;
3172 ValueType currentError_;
3173 ValueType missingDependents_;
3174 bool valid_;
3175 unsigned flags_;
3176 unsigned depth_;
3177};
3178
3179typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
3180
3182// SchemaValidatingReader
3183
3185
3194template <
3195 unsigned parseFlags,
3196 typename InputStream,
3197 typename SourceEncoding,
3198 typename SchemaDocumentType = SchemaDocument,
3199 typename StackAllocator = CrtAllocator>
3201public:
3202 typedef typename SchemaDocumentType::PointerType PointerType;
3203 typedef typename InputStream::Ch Ch;
3205
3207
3211 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
3212
3213 template <typename Handler>
3214 bool operator()(Handler& handler) {
3217 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
3218
3219 isValid_ = validator.IsValid();
3220 if (isValid_) {
3221 invalidSchemaPointer_ = PointerType();
3222 invalidSchemaKeyword_ = 0;
3223 invalidDocumentPointer_ = PointerType();
3224 error_.SetObject();
3225 }
3226 else {
3227 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
3228 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
3229 invalidSchemaCode_ = validator.GetInvalidSchemaCode();
3230 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
3231 error_.CopyFrom(validator.GetError(), allocator_);
3232 }
3233
3234 return parseResult_;
3235 }
3236
3237 const ParseResult& GetParseResult() const { return parseResult_; }
3238 bool IsValid() const { return isValid_; }
3239 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
3240 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
3241 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
3242 const ValueType& GetError() const { return error_; }
3243 ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
3244
3245private:
3246 InputStream& is_;
3247 const SchemaDocumentType& sd_;
3248
3249 ParseResult parseResult_;
3250 PointerType invalidSchemaPointer_;
3251 const Ch* invalidSchemaKeyword_;
3252 PointerType invalidDocumentPointer_;
3253 ValidateErrorCode invalidSchemaCode_;
3254 StackAllocator allocator_;
3255 ValueType error_;
3256 bool isValid_;
3257};
3258
3260RAPIDJSON_DIAG_POP
3261
3262#endif // RAPIDJSON_SCHEMA_H_
C-runtime library allocator.
Definition allocators.h:84
Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
Definition pointer.h:75
SAX-style JSON parser. Use Reader for UTF8 encoding and default allocator.
Definition reader.h:540
JSON schema document.
Definition schema.h:1816
GenericSchemaDocument(const ValueType &document, const Ch *uri=0, SizeType uriLength=0, IRemoteSchemaDocumentProviderType *remoteProvider=0, Allocator *allocator=0, const PointerType &pointer=PointerType(), const Specification &spec=Specification(kDraft04))
Constructor.
Definition schema.h:1844
ValueType * FindId(const ValueType &doc, const UriType &finduri, PointerType &resptr, const UriType &baseuri, bool full, const PointerType &here=PointerType()) const
Find the first subschema with a resolved 'id' that matches the specified URI.
Definition schema.h:2325
const SchemaType & GetRoot() const
Get the root schema.
Definition schema.h:1956
~GenericSchemaDocument()
Destructor.
Definition schema.h:1919
void SchemaError(const SchemaErrorCode code, const PointerType &location)
Default error method.
Definition schema.h:1982
void SchemaErrorValue(const SchemaErrorCode code, const PointerType &location, const Ch *value, SizeType length)
Method for error with single string value insert.
Definition schema.h:1988
GenericSchemaDocument & operator=(const GenericSchemaDocument &)
Prohibit assignment.
static const Specification GetSpecification(const ValueType &document)
Static method to get the specification of any schema document.
Definition schema.h:1943
GValue & GetError()
Gets the error object.
Definition schema.h:1959
GenericSchemaDocument(const GenericSchemaDocument &)
Prohibit copying.
void SchemaErrorPointer(const SchemaErrorCode code, const PointerType &location, const Ch *value, SizeType length, const PointerType &pointer)
Method for error with invalid pointer.
Definition schema.h:1995
JSON Schema Validator.
Definition schema.h:2441
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor without output handler.
Definition schema.h:2458
ValueType & GetError()
End of Implementation of ISchemaValidator.
Definition schema.h:2550
~GenericSchemaValidator()
Destructor.
Definition schema.h:2513
void Reset()
Reset the internal states.
Definition schema.h:2519
PointerType GetInvalidSchemaPointer() const
Gets the JSON pointer pointed to the invalid schema.
Definition schema.h:2555
void ResetError()
Reset the error state.
Definition schema.h:2527
const Ch * GetInvalidSchemaKeyword() const
Gets the keyword of invalid schema.
Definition schema.h:2561
PointerType GetInvalidDocumentPointer() const
Gets the JSON pointer pointed to the invalid value.
Definition schema.h:2577
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, OutputHandler &outputHandler, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor with output handler.
Definition schema.h:2488
ValidateErrorCode GetInvalidSchemaCode() const
Gets the error code of invalid schema.
Definition schema.h:2569
void SetValidateFlags(unsigned flags)
Implementation of ISchemaValidator.
Definition schema.h:2535
Represents an in-memory output stream.
size_t GetSize() const
Get the size of string in bytes in the string buffer.
GenericUri Resolve(const GenericUri &baseuri, Allocator *allocator=0)
Resolve this URI against another (base) URI in accordance with URI resolution rules.
Definition uri.h:157
Represents a JSON value. Use Value for UTF8 encoding and default allocator.
Definition document.h:669
GenericMemberIterator< false, EncodingType, AllocatorType >::Iterator MemberIterator
Definition document.h:677
const GenericValue * ConstValueIterator
Definition document.h:680
GenericMemberIterator< true, UTF8<>, RAPIDJSON_DEFAULT_ALLOCATOR >::Iterator ConstMemberIterator
Definition document.h:678
Default memory allocator used by the parser and DOM.
Definition allocators.h:131
SchemaValidatingReader(InputStream &is, const SchemaDocumentType &sd)
Constructor.
Definition schema.h:3211
Regular expression engine with subset of ECMAscript grammar.
Definition regex.h:111
A type-unsafe stack for storing different types of data.
Definition stack.h:38
Concept for allocating, resizing and freeing memory block.
Concept for receiving events from GenericReader upon parsing. The functions return true if no error o...
GenericValue< UTF8<> > Value
GenericValue with UTF8 encoding.
Definition document.h:2487
#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS
User-defined kValidateDefaultFlags definition.
Definition schema.h:175
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition rapidjson.h:438
#define RAPIDJSON_NAMESPACE_BEGIN
provide custom rapidjson namespace (opening expression)
Definition rapidjson.h:122
#define RAPIDJSON_NAMESPACE_END
provide custom rapidjson namespace (closing expression)
Definition rapidjson.h:125
ValidateErrorCode
Error codes when validating.
Definition error.h:163
SchemaErrorCode
Error codes when validating.
Definition error.h:221
@ kValidateErrorMinProperties
Object has less members than 'minProperties' value.
Definition error.h:183
@ kValidateErrorExclusiveMinimum
Number is less than or equal to the 'minimum' value.
Definition error.h:171
@ kValidateErrorAdditionalItems
Array has additional items that are not allowed by the schema.
Definition error.h:180
@ kValidateErrorMaxProperties
Object has more members than 'maxProperties' value.
Definition error.h:182
@ kValidateErrorOneOfMatch
Property matched more than one of the sub-schemas specified by 'oneOf'.
Definition error.h:193
@ kValidateErrorRequired
Object is missing one or more members required by the schema.
Definition error.h:184
@ kValidateErrorDependencies
Object has missing property or schema dependencies.
Definition error.h:187
@ kValidateErrorUniqueItems
Array has duplicate items but 'uniqueItems' is true.
Definition error.h:179
@ kValidateErrorReadOnly
Property is read-only but has been provided when validation is for writing.
Definition error.h:198
@ kValidateErrorEnum
Property has a value that is not one of its allowed enumerated values.
Definition error.h:189
@ kValidateErrorExclusiveMaximum
Number is greater than or equal to the 'maximum' value.
Definition error.h:169
@ kValidateErrorType
Property has a type that is not allowed by the schema.
Definition error.h:190
@ kValidateErrorOneOf
Property did not match any of the sub-schemas specified by 'oneOf'.
Definition error.h:192
@ kValidateErrorMinLength
String is longer than the 'maxLength' value.
Definition error.h:174
@ kValidateErrors
Top level error code when kValidateContinueOnErrorsFlag set.
Definition error.h:164
@ kValidateErrorMaxLength
String is longer than the 'maxLength' value.
Definition error.h:173
@ kValidateErrorWriteOnly
Property is write-only but has been provided when validation is for reading.
Definition error.h:199
@ kValidateErrorAnyOf
Property did not match any of the sub-schemas specified by 'anyOf'.
Definition error.h:195
@ kValidateErrorPattern
String does not match the 'pattern' regular expression.
Definition error.h:175
@ kValidateErrorMaximum
Number is greater than the 'maximum' value.
Definition error.h:168
@ kValidateErrorMaxItems
Array is longer than the 'maxItems' value.
Definition error.h:177
@ kValidateErrorMinimum
Number is less than the 'minimum' value.
Definition error.h:170
@ kValidateErrorMultipleOf
Number is not a multiple of the 'multipleOf' value.
Definition error.h:167
@ kValidateErrorNone
No error.
Definition error.h:165
@ kValidateErrorMinItems
Array is shorter than the 'minItems' value.
Definition error.h:178
@ kValidateErrorNot
Property matched the sub-schema specified by 'not'.
Definition error.h:196
@ kValidateErrorAdditionalProperties
Object has additional members that are not allowed by the schema.
Definition error.h:185
@ kValidateErrorPatternProperties
See other errors.
Definition error.h:186
@ kValidateErrorAllOf
Property did not match all of the sub-schemas specified by 'allOf'.
Definition error.h:194
@ kSchemaErrorStartUnknown
Pointer to start of schema does not resolve to a location in the document.
Definition error.h:224
@ kSchemaErrorSpecIllegal
Both JSON schema draft and OpenAPI version found in document.
Definition error.h:235
@ kSchemaErrorSpecUnsupported
JSON schema draft or OpenAPI version is not supported.
Definition error.h:234
@ kSchemaErrorRefPlainName
$ref fragment must be a JSON pointer
Definition error.h:225
@ kSchemaErrorRefNoRemoteProvider
$ref is remote but there is no remote provider
Definition error.h:230
@ kSchemaErrorSpecUnknown
JSON schema draft or OpenAPI version is not recognized.
Definition error.h:233
@ kSchemaErrorRefUnknown
$ref does not resolve to a location in the target document
Definition error.h:228
@ kSchemaErrorRefPointerInvalid
$ref fragment is not a valid JSON pointer at offset
Definition error.h:227
@ kSchemaErrorRefNoRemoteSchema
$ref is remote but the remote provider did not return a schema
Definition error.h:231
@ kSchemaErrorRefCyclical
$ref is cyclical
Definition error.h:229
@ kSchemaErrorRefInvalid
$ref must not be an empty string
Definition error.h:226
@ kSchemaErrorRegexInvalid
Invalid regular expression in 'pattern' or 'patternProperties'.
Definition error.h:232
@ kSchemaErrorReadOnlyAndWriteOnly
Property must not be both 'readOnly' and 'writeOnly'.
Definition error.h:236
Integer len(const char *s)
Retourne la longueur de la chaîne s.
Type
Type of JSON value.
Definition rapidjson.h:730
@ kFalseType
false
Definition rapidjson.h:732
@ kObjectType
object
Definition rapidjson.h:734
@ kTrueType
true
Definition rapidjson.h:733
@ kStringType
string
Definition rapidjson.h:736
@ kNullType
null
Definition rapidjson.h:731
@ kArrayType
array
Definition rapidjson.h:735
@ kNumberType
number
Definition rapidjson.h:737
#define RAPIDJSON_DELETE(x)
! customization point for global delete
Definition rapidjson.h:717
RAPIDJSON_NAMESPACE_BEGIN typedef unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition rapidjson.h:416
#define RAPIDJSON_UINT64_C2(high32, low32)
Construct a 64-bit literal by a pair of 32-bit integer.
Definition rapidjson.h:321
#define RAPIDJSON_NEW(TypeName)
! customization point for global new
Definition rapidjson.h:713
Default implementation of Handler.
Definition reader.h:199
Reference to a constant string (not taking a copy)
Definition document.h:347