Loading...
Searching...
No Matches
schema.h
1// Tencent is pleased to support the open source community by making RapidJSON available->
2//
3// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
4//
5// Licensed under the MIT License (the "License"); you may not use this file except
6// in compliance with the License-> You may obtain a copy of the License at
7//
8// http://opensource->org/licenses/MIT
9//
10// Unless required by applicable law or agreed to in writing, software distributed
11// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12// CONDITIONS OF ANY KIND, either express or implied-> See the License for the
13// specific language governing permissions and limitations under the License->
14
15#ifndef RAPIDJSON_SCHEMA_H_
16#define RAPIDJSON_SCHEMA_H_
17
18#include "document.h"
19#include "pointer.h"
20#include "stringbuffer.h"
21#include "error/en.h"
22#include "uri.h"
23#include <cmath> // abs, floor
24
25#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
26#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
27#endif
28
29#if !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) || !(__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
30#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
31#endif
32
33#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
34#include "internal/regex.h"
35#elif RAPIDJSON_SCHEMA_USE_STDREGEX
36#include <regex>
37#endif
38
39#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
40#define RAPIDJSON_SCHEMA_HAS_REGEX 1
41#else
42#define RAPIDJSON_SCHEMA_HAS_REGEX 0
43#endif
44
45#ifndef RAPIDJSON_SCHEMA_VERBOSE
46#define RAPIDJSON_SCHEMA_VERBOSE 0
47#endif
48
49RAPIDJSON_DIAG_PUSH
50
51#if defined(__GNUC__)
52RAPIDJSON_DIAG_OFF(effc++)
53#endif
54
55#ifdef __clang__
56RAPIDJSON_DIAG_OFF(weak-vtables)
57RAPIDJSON_DIAG_OFF(exit-time-destructors)
58RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
59RAPIDJSON_DIAG_OFF(variadic-macros)
60#elif defined(_MSC_VER)
61RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
62#endif
63
64RAPIDJSON_NAMESPACE_BEGIN
65
66///////////////////////////////////////////////////////////////////////////////
67// Verbose Utilities
68
69#if RAPIDJSON_SCHEMA_VERBOSE
70
71namespace internal {
72
73inline void PrintInvalidKeywordData(const char* keyword) {
74 printf(" Fail keyword: '%s'\n", keyword);
75}
76
77inline void PrintInvalidKeywordData(const wchar_t* keyword) {
78 wprintf(L" Fail keyword: '%ls'\n", keyword);
79}
80
81inline void PrintInvalidDocumentData(const char* document) {
82 printf(" Fail document: '%s'\n", document);
83}
84
85inline void PrintInvalidDocumentData(const wchar_t* document) {
86 wprintf(L" Fail document: '%ls'\n", document);
87}
88
89inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) {
90 printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d);
91}
92
93inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) {
94 wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d);
95}
96
97inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) {
98 printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved);
99}
100
101inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) {
102 wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved);
103}
104
105inline void PrintMethodData(const char* method) {
106 printf("%s\n", method);
107}
108
109inline void PrintMethodData(const char* method, bool b) {
110 printf("%s, Data: '%s'\n", method, b ? "true" : "false");
111}
112
113inline void PrintMethodData(const char* method, int64_t i) {
114 printf("%s, Data: '%" PRId64 "'\n", method, i);
115}
116
117inline void PrintMethodData(const char* method, uint64_t u) {
118 printf("%s, Data: '%" PRIu64 "'\n", method, u);
119}
120
121inline void PrintMethodData(const char* method, double d) {
122 printf("%s, Data: '%lf'\n", method, d);
123}
124
125inline void PrintMethodData(const char* method, const char* s) {
126 printf("%s, Data: '%s'\n", method, s);
127}
128
129inline void PrintMethodData(const char* method, const wchar_t* s) {
130 wprintf(L"%hs, Data: '%ls'\n", method, s);
131}
132
133inline void PrintMethodData(const char* method, const char* s1, const char* s2) {
134 printf("%s, Data: '%s', '%s'\n", method, s1, s2);
135}
136
137inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) {
138 wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2);
139}
140
141} // namespace internal
142
143#endif // RAPIDJSON_SCHEMA_VERBOSE
144
145#ifndef RAPIDJSON_SCHEMA_PRINT
146#if RAPIDJSON_SCHEMA_VERBOSE
147#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__)
148#else
149#define RAPIDJSON_SCHEMA_PRINT(name, ...)
150#endif
151#endif
152
153///////////////////////////////////////////////////////////////////////////////
154// RAPIDJSON_INVALID_KEYWORD_RETURN
155
156#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
157RAPIDJSON_MULTILINEMACRO_BEGIN\
158 context.invalidCode = code;\
159 context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
160 RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\
161 return false;\
162RAPIDJSON_MULTILINEMACRO_END
163
164///////////////////////////////////////////////////////////////////////////////
165// ValidateFlag
166
167/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
168 \ingroup RAPIDJSON_CONFIG
169 \brief User-defined kValidateDefaultFlags definition.
170
171 User can define this as any \c ValidateFlag combinations.
172*/
173#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
174#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
175#endif
176
177//! Combination of validate flags
179 kValidateNoFlags = 0, //!< No flags are set.
180 kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
181 kValidateReadFlag = 2, //!< Validation is for a read semantic.
182 kValidateWriteFlag = 4, //!< Validation is for a write semantic.
183 kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
185
186///////////////////////////////////////////////////////////////////////////////
187// Specification
189 kDraftUnknown = -1,
190 kDraftNone = 0,
191 kDraft03 = 3,
192 kDraftMin = 4, //!< Current minimum supported draft
193 kDraft04 = 4,
194 kDraft05 = 5,
195 kDraftMax = 5, //!< Current maximum supported draft
196 kDraft06 = 6,
197 kDraft07 = 7,
198 kDraft2019_09 = 8,
199 kDraft2020_12 = 9
200};
201
203 kVersionUnknown = -1,
204 kVersionNone = 0,
205 kVersionMin = 2, //!< Current minimum supported version
206 kVersion20 = 2,
207 kVersion30 = 3,
208 kVersionMax = 3, //!< Current maximum supported version
209 kVersion31 = 4,
210};
211
213 Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {}
214 Specification(OpenApiVersion o) : oapi(o) {
215 if (oapi == kVersion20) draft = kDraft04;
216 else if (oapi == kVersion30) draft = kDraft05;
217 else if (oapi == kVersion31) draft = kDraft2020_12;
218 else draft = kDraft04;
219 }
220 ~Specification() {}
221 bool IsSupported() const {
222 return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax)));
223 }
224 SchemaDraft draft;
225 OpenApiVersion oapi;
226};
227
228///////////////////////////////////////////////////////////////////////////////
229// Forward declarations
230
231template <typename ValueType, typename Allocator>
233
234namespace internal {
235
236template <typename SchemaDocumentType>
237class Schema;
238
239///////////////////////////////////////////////////////////////////////////////
240// ISchemaValidator
241
242class ISchemaValidator {
243public:
244 virtual ~ISchemaValidator() {}
245 virtual bool IsValid() const = 0;
246 virtual void SetValidateFlags(unsigned flags) = 0;
247 virtual unsigned GetValidateFlags() const = 0;
248};
249
250///////////////////////////////////////////////////////////////////////////////
251// ISchemaStateFactory
252
253template <typename SchemaType>
254class ISchemaStateFactory {
255public:
256 virtual ~ISchemaStateFactory() {}
257 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
258 virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
259 virtual void* CreateHasher() = 0;
260 virtual uint64_t GetHashCode(void* hasher) = 0;
261 virtual void DestroryHasher(void* hasher) = 0;
262 virtual void* MallocState(size_t size) = 0;
263 virtual void FreeState(void* p) = 0;
264};
265
266///////////////////////////////////////////////////////////////////////////////
267// IValidationErrorHandler
268
269template <typename SchemaType>
270class IValidationErrorHandler {
271public:
272 typedef typename SchemaType::Ch Ch;
273 typedef typename SchemaType::SValue SValue;
274
275 virtual ~IValidationErrorHandler() {}
276
277 virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
278 virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
279 virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
280 virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
281 virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
282 virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
283 virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
284 virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
285 virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
286
287 virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
288 virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
289 virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
290
291 virtual void DisallowedItem(SizeType index) = 0;
292 virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
293 virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
294 virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
295
296 virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
297 virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
298 virtual void StartMissingProperties() = 0;
299 virtual void AddMissingProperty(const SValue& name) = 0;
300 virtual bool EndMissingProperties() = 0;
301 virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
302 virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
303
304 virtual void StartDependencyErrors() = 0;
305 virtual void StartMissingDependentProperties() = 0;
306 virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
307 virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
308 virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
309 virtual bool EndDependencyErrors() = 0;
310
311 virtual void DisallowedValue(const ValidateErrorCode code) = 0;
312 virtual void StartDisallowedType() = 0;
313 virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
314 virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
315 virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
316 virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
317 virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
318 virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0;
319 virtual void Disallowed() = 0;
320 virtual void DisallowedWhenWriting() = 0;
321 virtual void DisallowedWhenReading() = 0;
322};
323
324
325///////////////////////////////////////////////////////////////////////////////
326// Hasher
327
328// For comparison of compound value
329template<typename Encoding, typename Allocator>
330class Hasher {
331public:
332 typedef typename Encoding::Ch Ch;
333
334 Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
335
336 bool Null() { return WriteType(kNullType); }
337 bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
338 bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
339 bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
340 bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
341 bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
342 bool Double(double d) {
343 Number n;
344 if (d < 0) n.u.i = static_cast<int64_t>(d);
345 else n.u.u = static_cast<uint64_t>(d);
346 n.d = d;
347 return WriteNumber(n);
348 }
349
350 bool RawNumber(const Ch* str, SizeType len, bool) {
351 WriteBuffer(kNumberType, str, len * sizeof(Ch));
352 return true;
353 }
354
355 bool String(const Ch* str, SizeType len, bool) {
356 WriteBuffer(kStringType, str, len * sizeof(Ch));
357 return true;
358 }
359
360 bool StartObject() { return true; }
361 bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
362 bool EndObject(SizeType memberCount) {
363 uint64_t h = Hash(0, kObjectType);
364 uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
365 for (SizeType i = 0; i < memberCount; i++)
366 // Issue #2205
367 // Hasing the key to avoid key=value cases with bug-prone zero-value hash
368 h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]); // Use xor to achieve member order insensitive
369 *stack_.template Push<uint64_t>() = h;
370 return true;
371 }
372
373 bool StartArray() { return true; }
374 bool EndArray(SizeType elementCount) {
375 uint64_t h = Hash(0, kArrayType);
376 uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
377 for (SizeType i = 0; i < elementCount; i++)
378 h = Hash(h, e[i]); // Use hash to achieve element order sensitive
379 *stack_.template Push<uint64_t>() = h;
380 return true;
381 }
382
383 bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
384
385 uint64_t GetHashCode() const {
386 RAPIDJSON_ASSERT(IsValid());
387 return *stack_.template Top<uint64_t>();
388 }
389
390private:
391 static const size_t kDefaultSize = 256;
392 struct Number {
393 union U {
394 uint64_t u;
395 int64_t i;
396 }u;
397 double d;
398 };
399
400 bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
401
402 bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
403
404 bool WriteBuffer(Type type, const void* data, size_t len) {
405 // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
406 uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type);
407 const unsigned char* d = static_cast<const unsigned char*>(data);
408 for (size_t i = 0; i < len; i++)
409 h = Hash(h, d[i]);
410 *stack_.template Push<uint64_t>() = h;
411 return true;
412 }
413
414 static uint64_t Hash(uint64_t h, uint64_t d) {
415 static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
416 h ^= d;
417 h *= kPrime;
418 return h;
419 }
420
421 Stack<Allocator> stack_;
422};
423
424///////////////////////////////////////////////////////////////////////////////
425// SchemaValidationContext
426
427template <typename SchemaDocumentType>
428struct SchemaValidationContext {
429 typedef Schema<SchemaDocumentType> SchemaType;
430 typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
431 typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
432 typedef typename SchemaType::ValueType ValueType;
433 typedef typename ValueType::Ch Ch;
434
435 enum PatternValidatorType {
436 kPatternValidatorOnly,
437 kPatternValidatorWithProperty,
438 kPatternValidatorWithAdditionalProperty
439 };
440
441 SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) :
442 factory(f),
443 error_handler(eh),
444 schema(s),
445 flags(fl),
446 valueSchema(),
447 invalidKeyword(),
448 invalidCode(),
449 hasher(),
450 arrayElementHashCodes(),
451 validators(),
452 validatorCount(),
453 patternPropertiesValidators(),
454 patternPropertiesValidatorCount(),
455 patternPropertiesSchemas(),
456 patternPropertiesSchemaCount(),
457 valuePatternValidatorType(kPatternValidatorOnly),
458 propertyExist(),
459 inArray(false),
460 valueUniqueness(false),
461 arrayUniqueness(false)
462 {
463 }
464
465 ~SchemaValidationContext() {
466 if (hasher)
467 factory.DestroryHasher(hasher);
468 if (validators) {
469 for (SizeType i = 0; i < validatorCount; i++) {
470 if (validators[i]) {
471 factory.DestroySchemaValidator(validators[i]);
472 }
473 }
474 factory.FreeState(validators);
475 }
476 if (patternPropertiesValidators) {
477 for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) {
478 if (patternPropertiesValidators[i]) {
479 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
480 }
481 }
482 factory.FreeState(patternPropertiesValidators);
483 }
484 if (patternPropertiesSchemas)
485 factory.FreeState(patternPropertiesSchemas);
486 if (propertyExist)
487 factory.FreeState(propertyExist);
488 }
489
490 SchemaValidatorFactoryType& factory;
491 ErrorHandlerType& error_handler;
492 const SchemaType* schema;
493 unsigned flags;
494 const SchemaType* valueSchema;
495 const Ch* invalidKeyword;
496 ValidateErrorCode invalidCode;
497 void* hasher; // Only validator access
498 void* arrayElementHashCodes; // Only validator access this
499 ISchemaValidator** validators;
500 SizeType validatorCount;
501 ISchemaValidator** patternPropertiesValidators;
502 SizeType patternPropertiesValidatorCount;
503 const SchemaType** patternPropertiesSchemas;
504 SizeType patternPropertiesSchemaCount;
505 PatternValidatorType valuePatternValidatorType;
506 PatternValidatorType objectPatternValidatorType;
507 SizeType arrayElementIndex;
508 bool* propertyExist;
509 bool inArray;
510 bool valueUniqueness;
511 bool arrayUniqueness;
512};
513
514///////////////////////////////////////////////////////////////////////////////
515// Schema
516
517template <typename SchemaDocumentType>
518class Schema {
519public:
520 typedef typename SchemaDocumentType::ValueType ValueType;
521 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
522 typedef typename SchemaDocumentType::PointerType PointerType;
523 typedef typename ValueType::EncodingType EncodingType;
524 typedef typename EncodingType::Ch Ch;
525 typedef SchemaValidationContext<SchemaDocumentType> Context;
526 typedef Schema<SchemaDocumentType> SchemaType;
527 typedef GenericValue<EncodingType, AllocatorType> SValue;
528 typedef IValidationErrorHandler<Schema> ErrorHandler;
529 typedef GenericUri<ValueType, AllocatorType> UriType;
530 friend class GenericSchemaDocument<ValueType, AllocatorType>;
531
532 Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
533 allocator_(allocator),
534 uri_(schemaDocument->GetURI(), *allocator),
535 id_(id, allocator),
536 spec_(schemaDocument->GetSpecification()),
537 pointer_(p, allocator),
538 typeless_(schemaDocument->GetTypeless()),
539 enum_(),
540 enumCount_(),
541 not_(),
542 type_((1 << kTotalSchemaType) - 1), // typeless
543 validatorCount_(),
544 notValidatorIndex_(),
545 properties_(),
546 additionalPropertiesSchema_(),
547 patternProperties_(),
548 patternPropertyCount_(),
549 propertyCount_(),
550 minProperties_(),
551 maxProperties_(SizeType(~0)),
552 additionalProperties_(true),
553 hasDependencies_(),
554 hasRequired_(),
555 hasSchemaDependencies_(),
556 additionalItemsSchema_(),
557 itemsList_(),
558 itemsTuple_(),
559 itemsTupleCount_(),
560 minItems_(),
561 maxItems_(SizeType(~0)),
562 additionalItems_(true),
563 uniqueItems_(false),
564 pattern_(),
565 minLength_(0),
566 maxLength_(~SizeType(0)),
567 exclusiveMinimum_(false),
568 exclusiveMaximum_(false),
569 defaultValueLength_(0),
570 readOnly_(false),
571 writeOnly_(false),
572 nullable_(false)
573 {
574 GenericStringBuffer<EncodingType> sb;
575 p.StringifyUriFragment(sb);
576 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString());
577
578 typedef typename ValueType::ConstValueIterator ConstValueIterator;
579 typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
580
581 // PR #1393
582 // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite
583 // recursion (with recursive schemas), since schemaDocument->getSchema() is always
584 // checked before creating a new one. Don't cache typeless_, though.
585 if (this != typeless_) {
586 typedef typename SchemaDocumentType::SchemaEntry SchemaEntry;
587 SchemaEntry *entry = schemaDocument->schemaMap_.template Push<SchemaEntry>();
588 new (entry) SchemaEntry(pointer_, this, true, allocator_);
589 schemaDocument->AddSchemaRefs(this);
590 }
591
592 if (!value.IsObject())
593 return;
594
595 // If we have an id property, resolve it with the in-scope id
596 // Not supported for open api 2.0 or 3.0
597 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
598 if (const ValueType* v = GetMember(value, GetIdString())) {
599 if (v->IsString()) {
600 UriType local(*v, allocator);
601 id_ = local.Resolve(id_, allocator);
602 RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString());
603 }
604 }
605
606 if (const ValueType* v = GetMember(value, GetTypeString())) {
607 type_ = 0;
608 if (v->IsString())
609 AddType(*v);
610 else if (v->IsArray())
611 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
612 AddType(*itr);
613 }
614
615 if (const ValueType* v = GetMember(value, GetEnumString())) {
616 if (v->IsArray() && v->Size() > 0) {
617 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
618 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
619 typedef Hasher<EncodingType, MemoryPoolAllocator<AllocatorType> > EnumHasherType;
620 char buffer[256u + 24];
621 MemoryPoolAllocator<AllocatorType> hasherAllocator(buffer, sizeof(buffer));
622 EnumHasherType h(&hasherAllocator, 256);
623 itr->Accept(h);
624 enum_[enumCount_++] = h.GetHashCode();
625 }
626 }
627 }
628
629 if (schemaDocument)
630 AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
631
632 // AnyOf, OneOf, Not not supported for open api 2.0
633 if (schemaDocument && spec_.oapi != kVersion20) {
634 AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
635 AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
636
637 if (const ValueType* v = GetMember(value, GetNotString())) {
638 schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document, id_);
639 notValidatorIndex_ = validatorCount_;
640 validatorCount_++;
641 }
642 }
643
644 // Object
645
646 const ValueType* properties = GetMember(value, GetPropertiesString());
647 const ValueType* required = GetMember(value, GetRequiredString());
648 const ValueType* dependencies = GetMember(value, GetDependenciesString());
649 {
650 // Gather properties from properties/required/dependencies
651 SValue allProperties(kArrayType);
652
653 if (properties && properties->IsObject())
654 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
655 AddUniqueElement(allProperties, itr->name);
656
657 if (required && required->IsArray())
658 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
659 if (itr->IsString())
660 AddUniqueElement(allProperties, *itr);
661
662 // Dependencies not supported for open api 2.0 and 3.0
663 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
664 if (dependencies && dependencies->IsObject())
665 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
666 AddUniqueElement(allProperties, itr->name);
667 if (itr->value.IsArray())
668 for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
669 if (i->IsString())
670 AddUniqueElement(allProperties, *i);
671 }
672
673 if (allProperties.Size() > 0) {
674 propertyCount_ = allProperties.Size();
675 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
676 for (SizeType i = 0; i < propertyCount_; i++) {
677 new (&properties_[i]) Property();
678 properties_[i].name = allProperties[i];
679 properties_[i].schema = typeless_;
680 }
681 }
682 }
683
684 if (properties && properties->IsObject()) {
685 PointerType q = p.Append(GetPropertiesString(), allocator_);
686 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
687 SizeType index;
688 if (FindPropertyIndex(itr->name, &index))
689 schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
690 }
691 }
692
693 // PatternProperties not supported for open api 2.0 and 3.0
694 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
695 if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
696 PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
697 patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
698 patternPropertyCount_ = 0;
699
700 for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
701 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
702 PointerType r = q.Append(itr->name, allocator_);
703 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r);
704 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_);
705 patternPropertyCount_++;
706 }
707 }
708
709 if (required && required->IsArray())
710 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
711 if (itr->IsString()) {
712 SizeType index;
713 if (FindPropertyIndex(*itr, &index)) {
714 properties_[index].required = true;
715 hasRequired_ = true;
716 }
717 }
718
719 // Dependencies not supported for open api 2.0 and 3.0
720 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
721 if (dependencies && dependencies->IsObject()) {
722 PointerType q = p.Append(GetDependenciesString(), allocator_);
723 hasDependencies_ = true;
724 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
725 SizeType sourceIndex;
726 if (FindPropertyIndex(itr->name, &sourceIndex)) {
727 if (itr->value.IsArray()) {
728 properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
729 std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
730 for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
731 SizeType targetIndex;
732 if (FindPropertyIndex(*targetItr, &targetIndex))
733 properties_[sourceIndex].dependencies[targetIndex] = true;
734 }
735 }
736 else if (itr->value.IsObject()) {
737 hasSchemaDependencies_ = true;
738 schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_);
739 properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
740 validatorCount_++;
741 }
742 }
743 }
744 }
745
746 if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
747 if (v->IsBool())
748 additionalProperties_ = v->GetBool();
749 else if (v->IsObject())
750 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_);
751 }
752
753 AssignIfExist(minProperties_, value, GetMinPropertiesString());
754 AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
755
756 // Array
757 if (const ValueType* v = GetMember(value, GetItemsString())) {
758 PointerType q = p.Append(GetItemsString(), allocator_);
759 if (v->IsObject()) // List validation
760 schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_);
761 else if (v->IsArray()) { // Tuple validation
762 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
763 SizeType index = 0;
764 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
765 schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_);
766 }
767 }
768
769 AssignIfExist(minItems_, value, GetMinItemsString());
770 AssignIfExist(maxItems_, value, GetMaxItemsString());
771
772 // AdditionalItems not supported for openapi 2.0 and 3.0
773 if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
774 if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
775 if (v->IsBool())
776 additionalItems_ = v->GetBool();
777 else if (v->IsObject())
778 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_);
779 }
780
781 AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
782
783 // String
784 AssignIfExist(minLength_, value, GetMinLengthString());
785 AssignIfExist(maxLength_, value, GetMaxLengthString());
786
787 if (const ValueType* v = GetMember(value, GetPatternString()))
788 pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_));
789
790 // Number
791 if (const ValueType* v = GetMember(value, GetMinimumString()))
792 if (v->IsNumber())
793 minimum_.CopyFrom(*v, *allocator_);
794
795 if (const ValueType* v = GetMember(value, GetMaximumString()))
796 if (v->IsNumber())
797 maximum_.CopyFrom(*v, *allocator_);
798
799 AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
800 AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
801
802 if (const ValueType* v = GetMember(value, GetMultipleOfString()))
803 if (v->IsNumber() && v->GetDouble() > 0.0)
804 multipleOf_.CopyFrom(*v, *allocator_);
805
806 // Default
807 if (const ValueType* v = GetMember(value, GetDefaultValueString()))
808 if (v->IsString())
809 defaultValueLength_ = v->GetStringLength();
810
811 // ReadOnly - open api only (until draft 7 supported)
812 // WriteOnly - open api 3 only (until draft 7 supported)
813 // Both can't be true
814 if (spec_.oapi != kVersionNone)
815 AssignIfExist(readOnly_, value, GetReadOnlyString());
816 if (spec_.oapi >= kVersion30)
817 AssignIfExist(writeOnly_, value, GetWriteOnlyString());
818 if (readOnly_ && writeOnly_)
819 schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p);
820
821 // Nullable - open api 3 only
822 // If true add 'null' as allowable type
823 if (spec_.oapi >= kVersion30) {
824 AssignIfExist(nullable_, value, GetNullableString());
825 if (nullable_)
826 AddType(GetNullString());
827 }
828 }
829
830 ~Schema() {
831 AllocatorType::Free(enum_);
832 if (properties_) {
833 for (SizeType i = 0; i < propertyCount_; i++)
834 properties_[i].~Property();
835 AllocatorType::Free(properties_);
836 }
837 if (patternProperties_) {
838 for (SizeType i = 0; i < patternPropertyCount_; i++)
839 patternProperties_[i].~PatternProperty();
840 AllocatorType::Free(patternProperties_);
841 }
842 AllocatorType::Free(itemsTuple_);
843#if RAPIDJSON_SCHEMA_HAS_REGEX
844 if (pattern_) {
845 pattern_->~RegexType();
846 AllocatorType::Free(pattern_);
847 }
848#endif
849 }
850
851 const SValue& GetURI() const {
852 return uri_;
853 }
854
855 const UriType& GetId() const {
856 return id_;
857 }
858
859 const Specification& GetSpecification() const {
860 return spec_;
861 }
862
863 const PointerType& GetPointer() const {
864 return pointer_;
865 }
866
867 bool BeginValue(Context& context) const {
868 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue");
869 if (context.inArray) {
870 if (uniqueItems_)
871 context.valueUniqueness = true;
872
873 if (itemsList_)
874 context.valueSchema = itemsList_;
875 else if (itemsTuple_) {
876 if (context.arrayElementIndex < itemsTupleCount_)
877 context.valueSchema = itemsTuple_[context.arrayElementIndex];
878 else if (additionalItemsSchema_)
879 context.valueSchema = additionalItemsSchema_;
880 else if (additionalItems_)
881 context.valueSchema = typeless_;
882 else {
883 context.error_handler.DisallowedItem(context.arrayElementIndex);
884 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
885 context.valueSchema = typeless_;
886 // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
887 context.arrayElementIndex++;
888 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
889 }
890 }
891 else
892 context.valueSchema = typeless_;
893
894 context.arrayElementIndex++;
895 }
896 return true;
897 }
898
899 RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
900 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue");
901 // Only check pattern properties if we have validators
902 if (context.patternPropertiesValidatorCount > 0) {
903 bool otherValid = false;
904 SizeType count = context.patternPropertiesValidatorCount;
905 if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
906 otherValid = context.patternPropertiesValidators[--count]->IsValid();
907
908 bool patternValid = true;
909 for (SizeType i = 0; i < count; i++)
910 if (!context.patternPropertiesValidators[i]->IsValid()) {
911 patternValid = false;
912 break;
913 }
914
915 if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
916 if (!patternValid) {
917 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
918 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
919 }
920 }
921 else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
922 if (!patternValid || !otherValid) {
923 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
924 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
925 }
926 }
927 else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
928 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
929 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
930 }
931 }
932
933 // For enums only check if we have a hasher
934 if (enum_ && context.hasher) {
935 const uint64_t h = context.factory.GetHashCode(context.hasher);
936 for (SizeType i = 0; i < enumCount_; i++)
937 if (enum_[i] == h)
938 goto foundEnum;
939 context.error_handler.DisallowedValue(kValidateErrorEnum);
940 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
941 foundEnum:;
942 }
943
944 // Only check allOf etc if we have validators
945 if (context.validatorCount > 0) {
946 if (allOf_.schemas)
947 for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
948 if (!context.validators[i]->IsValid()) {
949 context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
950 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
951 }
952
953 if (anyOf_.schemas) {
954 for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
955 if (context.validators[i]->IsValid())
956 goto foundAny;
957 context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
958 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
959 foundAny:;
960 }
961
962 if (oneOf_.schemas) {
963 bool oneValid = false;
964 SizeType firstMatch = 0;
965 for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
966 if (context.validators[i]->IsValid()) {
967 if (oneValid) {
968 context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin);
969 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
970 } else {
971 oneValid = true;
972 firstMatch = i - oneOf_.begin;
973 }
974 }
975 if (!oneValid) {
976 context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
977 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
978 }
979 }
980
981 if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
982 context.error_handler.Disallowed();
983 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
984 }
985 }
986
987 return true;
988 }
989
990 bool Null(Context& context) const {
991 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null");
992 if (!(type_ & (1 << kNullSchemaType))) {
993 DisallowedType(context, GetNullString());
994 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
995 }
996 return CreateParallelValidator(context);
997 }
998
999 bool Bool(Context& context, bool b) const {
1000 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b);
1001 if (!CheckBool(context, b))
1002 return false;
1003 return CreateParallelValidator(context);
1004 }
1005
1006 bool Int(Context& context, int i) const {
1007 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i);
1008 if (!CheckInt(context, i))
1009 return false;
1010 return CreateParallelValidator(context);
1011 }
1012
1013 bool Uint(Context& context, unsigned u) const {
1014 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u);
1015 if (!CheckUint(context, u))
1016 return false;
1017 return CreateParallelValidator(context);
1018 }
1019
1020 bool Int64(Context& context, int64_t i) const {
1021 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i);
1022 if (!CheckInt(context, i))
1023 return false;
1024 return CreateParallelValidator(context);
1025 }
1026
1027 bool Uint64(Context& context, uint64_t u) const {
1028 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u);
1029 if (!CheckUint(context, u))
1030 return false;
1031 return CreateParallelValidator(context);
1032 }
1033
1034 bool Double(Context& context, double d) const {
1035 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d);
1036 if (!(type_ & (1 << kNumberSchemaType))) {
1037 DisallowedType(context, GetNumberString());
1038 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1039 }
1040
1041 if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
1042 return false;
1043
1044 if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
1045 return false;
1046
1047 if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
1048 return false;
1049
1050 return CreateParallelValidator(context);
1051 }
1052
1053 bool String(Context& context, const Ch* str, SizeType length, bool) const {
1054 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str);
1055 if (!(type_ & (1 << kStringSchemaType))) {
1056 DisallowedType(context, GetStringString());
1057 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1058 }
1059
1060 if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
1061 SizeType count;
1062 if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
1063 if (count < minLength_) {
1064 context.error_handler.TooShort(str, length, minLength_);
1065 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
1066 }
1067 if (count > maxLength_) {
1068 context.error_handler.TooLong(str, length, maxLength_);
1069 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
1070 }
1071 }
1072 }
1073
1074 if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
1075 context.error_handler.DoesNotMatch(str, length);
1076 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
1077 }
1078
1079 return CreateParallelValidator(context);
1080 }
1081
1082 bool StartObject(Context& context) const {
1083 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject");
1084 if (!(type_ & (1 << kObjectSchemaType))) {
1085 DisallowedType(context, GetObjectString());
1086 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1087 }
1088
1089 if (hasDependencies_ || hasRequired_) {
1090 context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
1091 std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
1092 }
1093
1094 if (patternProperties_) { // pre-allocate schema array
1095 SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
1096 context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
1097 context.patternPropertiesSchemaCount = 0;
1098 std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
1099 }
1100
1101 return CreateParallelValidator(context);
1102 }
1103
1104 bool Key(Context& context, const Ch* str, SizeType len, bool) const {
1105 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str);
1106
1107 if (patternProperties_) {
1108 context.patternPropertiesSchemaCount = 0;
1109 for (SizeType i = 0; i < patternPropertyCount_; i++)
1110 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
1111 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
1112 context.valueSchema = typeless_;
1113 }
1114 }
1115
1116 SizeType index = 0;
1117 if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
1118 if (context.patternPropertiesSchemaCount > 0) {
1119 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
1120 context.valueSchema = typeless_;
1121 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
1122 }
1123 else
1124 context.valueSchema = properties_[index].schema;
1125
1126 if (context.propertyExist)
1127 context.propertyExist[index] = true;
1128
1129 return true;
1130 }
1131
1132 if (additionalPropertiesSchema_) {
1133 if (context.patternPropertiesSchemaCount > 0) {
1134 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
1135 context.valueSchema = typeless_;
1136 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
1137 }
1138 else
1139 context.valueSchema = additionalPropertiesSchema_;
1140 return true;
1141 }
1142 else if (additionalProperties_) {
1143 context.valueSchema = typeless_;
1144 return true;
1145 }
1146
1147 if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
1148 // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
1149 context.valueSchema = typeless_;
1150 context.error_handler.DisallowedProperty(str, len);
1151 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
1152 }
1153
1154 return true;
1155 }
1156
1157 bool EndObject(Context& context, SizeType memberCount) const {
1158 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject");
1159 if (hasRequired_) {
1160 context.error_handler.StartMissingProperties();
1161 for (SizeType index = 0; index < propertyCount_; index++)
1162 if (properties_[index].required && !context.propertyExist[index])
1163 if (properties_[index].schema->defaultValueLength_ == 0 )
1164 context.error_handler.AddMissingProperty(properties_[index].name);
1165 if (context.error_handler.EndMissingProperties())
1166 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
1167 }
1168
1169 if (memberCount < minProperties_) {
1170 context.error_handler.TooFewProperties(memberCount, minProperties_);
1171 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
1172 }
1173
1174 if (memberCount > maxProperties_) {
1175 context.error_handler.TooManyProperties(memberCount, maxProperties_);
1176 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
1177 }
1178
1179 if (hasDependencies_) {
1180 context.error_handler.StartDependencyErrors();
1181 for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
1182 const Property& source = properties_[sourceIndex];
1183 if (context.propertyExist[sourceIndex]) {
1184 if (source.dependencies) {
1185 context.error_handler.StartMissingDependentProperties();
1186 for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
1187 if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
1188 context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
1189 context.error_handler.EndMissingDependentProperties(source.name);
1190 }
1191 else if (source.dependenciesSchema) {
1192 ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
1193 if (!dependenciesValidator->IsValid())
1194 context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
1195 }
1196 }
1197 }
1198 if (context.error_handler.EndDependencyErrors())
1199 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
1200 }
1201
1202 return true;
1203 }
1204
1205 bool StartArray(Context& context) const {
1206 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray");
1207 context.arrayElementIndex = 0;
1208 context.inArray = true; // Ensure we note that we are in an array
1209
1210 if (!(type_ & (1 << kArraySchemaType))) {
1211 DisallowedType(context, GetArrayString());
1212 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1213 }
1214
1215 return CreateParallelValidator(context);
1216 }
1217
1218 bool EndArray(Context& context, SizeType elementCount) const {
1219 RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray");
1220 context.inArray = false;
1221
1222 if (elementCount < minItems_) {
1223 context.error_handler.TooFewItems(elementCount, minItems_);
1224 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
1225 }
1226
1227 if (elementCount > maxItems_) {
1228 context.error_handler.TooManyItems(elementCount, maxItems_);
1229 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
1230 }
1231
1232 return true;
1233 }
1234
1235 static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
1236 switch (validateErrorCode) {
1237 case kValidateErrorMultipleOf: return GetMultipleOfString();
1238 case kValidateErrorMaximum: return GetMaximumString();
1239 case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
1240 case kValidateErrorMinimum: return GetMinimumString();
1241 case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
1242
1243 case kValidateErrorMaxLength: return GetMaxLengthString();
1244 case kValidateErrorMinLength: return GetMinLengthString();
1245 case kValidateErrorPattern: return GetPatternString();
1246
1247 case kValidateErrorMaxItems: return GetMaxItemsString();
1248 case kValidateErrorMinItems: return GetMinItemsString();
1249 case kValidateErrorUniqueItems: return GetUniqueItemsString();
1250 case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
1251
1252 case kValidateErrorMaxProperties: return GetMaxPropertiesString();
1253 case kValidateErrorMinProperties: return GetMinPropertiesString();
1254 case kValidateErrorRequired: return GetRequiredString();
1255 case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
1256 case kValidateErrorPatternProperties: return GetPatternPropertiesString();
1257 case kValidateErrorDependencies: return GetDependenciesString();
1258
1259 case kValidateErrorEnum: return GetEnumString();
1260 case kValidateErrorType: return GetTypeString();
1261
1262 case kValidateErrorOneOf: return GetOneOfString();
1263 case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
1264 case kValidateErrorAllOf: return GetAllOfString();
1265 case kValidateErrorAnyOf: return GetAnyOfString();
1266 case kValidateErrorNot: return GetNotString();
1267
1268 case kValidateErrorReadOnly: return GetReadOnlyString();
1269 case kValidateErrorWriteOnly: return GetWriteOnlyString();
1270
1271 default: return GetNullString();
1272 }
1273 }
1274
1275
1276 // Generate functions for string literal according to Ch
1277#define RAPIDJSON_STRING_(name, ...) \
1278 static const ValueType& Get##name##String() {\
1279 static const Ch s[] = { __VA_ARGS__, '\0' };\
1280 static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
1281 return v;\
1282 }
1283
1284 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
1285 RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
1286 RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
1287 RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
1288 RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
1289 RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
1290 RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
1291 RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
1292 RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
1293 RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
1294 RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
1295 RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
1296 RAPIDJSON_STRING_(Not, 'n', 'o', 't')
1297 RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1298 RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
1299 RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
1300 RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1301 RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1302 RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1303 RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
1304 RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
1305 RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
1306 RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
1307 RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
1308 RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
1309 RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
1310 RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
1311 RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
1312 RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
1313 RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
1314 RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
1315 RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
1316 RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
1317 RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
1318 RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a')
1319 RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
1320 RAPIDJSON_STRING_(Id, 'i', 'd')
1321 RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r')
1322 RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i')
1323 RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y')
1324 RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
1325 RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e')
1326
1327#undef RAPIDJSON_STRING_
1328
1329private:
1330 enum SchemaValueType {
1331 kNullSchemaType,
1332 kBooleanSchemaType,
1333 kObjectSchemaType,
1334 kArraySchemaType,
1335 kStringSchemaType,
1336 kNumberSchemaType,
1337 kIntegerSchemaType,
1338 kTotalSchemaType
1339 };
1340
1341#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1342 typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
1343#elif RAPIDJSON_SCHEMA_USE_STDREGEX
1344 typedef std::basic_regex<Ch> RegexType;
1345#else
1346 typedef char RegexType;
1347#endif
1348
1349 struct SchemaArray {
1350 SchemaArray() : schemas(), count() {}
1351 ~SchemaArray() { AllocatorType::Free(schemas); }
1352 const SchemaType** schemas;
1353 SizeType begin; // begin index of context.validators
1354 SizeType count;
1355 };
1356
1357 template <typename V1, typename V2>
1358 void AddUniqueElement(V1& a, const V2& v) {
1359 for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
1360 if (*itr == v)
1361 return;
1362 V1 c(v, *allocator_);
1363 a.PushBack(c, *allocator_);
1364 }
1365
1366 static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
1367 typename ValueType::ConstMemberIterator itr = value.FindMember(name);
1368 return itr != value.MemberEnd() ? &(itr->value) : 0;
1369 }
1370
1371 static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
1372 if (const ValueType* v = GetMember(value, name))
1373 if (v->IsBool())
1374 out = v->GetBool();
1375 }
1376
1377 static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
1378 if (const ValueType* v = GetMember(value, name))
1379 if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
1380 out = static_cast<SizeType>(v->GetUint64());
1381 }
1382
1383 void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
1384 if (const ValueType* v = GetMember(value, name)) {
1385 if (v->IsArray() && v->Size() > 0) {
1386 PointerType q = p.Append(name, allocator_);
1387 out.count = v->Size();
1388 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
1389 memset(out.schemas, 0, sizeof(Schema*)* out.count);
1390 for (SizeType i = 0; i < out.count; i++)
1391 schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_);
1392 out.begin = validatorCount_;
1393 validatorCount_ += out.count;
1394 }
1395 }
1396 }
1397
1398#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
1399 template <typename ValueType>
1400 RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
1401 if (value.IsString()) {
1402 RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
1403 if (!r->IsValid()) {
1404 sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
1405 r->~RegexType();
1406 AllocatorType::Free(r);
1407 r = 0;
1408 }
1409 return r;
1410 }
1411 return 0;
1412 }
1413
1414 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1415 GenericRegexSearch<RegexType> rs(*pattern);
1416 return rs.Search(str);
1417 }
1418#elif RAPIDJSON_SCHEMA_USE_STDREGEX
1419 template <typename ValueType>
1420 RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
1421 if (value.IsString()) {
1422 RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
1423 try {
1424 return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1425 }
1426 catch (const std::regex_error& e) {
1427 sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
1428 AllocatorType::Free(r);
1429 }
1430 }
1431 return 0;
1432 }
1433
1434 static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1435 std::match_results<const Ch*> r;
1436 return std::regex_search(str, str + length, r, *pattern);
1437 }
1438#else
1439 template <typename ValueType>
1440 RegexType* CreatePattern(const ValueType&) {
1441 return 0;
1442 }
1443
1444 static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1445#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1446
1447 void AddType(const ValueType& type) {
1448 if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1449 else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1450 else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1451 else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1452 else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1453 else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1454 else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1455 }
1456
1457 // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required.
1458 // Also creates a hasher for enums and array uniqueness, if required.
1459 // Also a useful place to add type-independent error checks.
1460 bool CreateParallelValidator(Context& context) const {
1461 if (enum_ || context.arrayUniqueness)
1462 context.hasher = context.factory.CreateHasher();
1463
1464 if (validatorCount_) {
1465 RAPIDJSON_ASSERT(context.validators == 0);
1466 context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1467 std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_);
1468 context.validatorCount = validatorCount_;
1469
1470 // Always return after first failure for these sub-validators
1471 if (allOf_.schemas)
1472 CreateSchemaValidators(context, allOf_, false);
1473
1474 if (anyOf_.schemas)
1475 CreateSchemaValidators(context, anyOf_, false);
1476
1477 if (oneOf_.schemas)
1478 CreateSchemaValidators(context, oneOf_, false);
1479
1480 if (not_)
1481 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
1482
1483 if (hasSchemaDependencies_) {
1484 for (SizeType i = 0; i < propertyCount_; i++)
1485 if (properties_[i].dependenciesSchema)
1486 context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
1487 }
1488 }
1489
1490 // Add any other type-independent checks here
1491 if (readOnly_ && (context.flags & kValidateWriteFlag)) {
1492 context.error_handler.DisallowedWhenWriting();
1493 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly);
1494 }
1495 if (writeOnly_ && (context.flags & kValidateReadFlag)) {
1496 context.error_handler.DisallowedWhenReading();
1497 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly);
1498 }
1499
1500 return true;
1501 }
1502
1503 void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
1504 for (SizeType i = 0; i < schemas.count; i++)
1505 context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
1506 }
1507
1508 // O(n)
1509 bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1510 SizeType len = name.GetStringLength();
1511 const Ch* str = name.GetString();
1512 for (SizeType index = 0; index < propertyCount_; index++)
1513 if (properties_[index].name.GetStringLength() == len &&
1514 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1515 {
1516 *outIndex = index;
1517 return true;
1518 }
1519 return false;
1520 }
1521
1522 bool CheckBool(Context& context, bool) const {
1523 if (!(type_ & (1 << kBooleanSchemaType))) {
1524 DisallowedType(context, GetBooleanString());
1525 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1526 }
1527 return true;
1528 }
1529
1530 bool CheckInt(Context& context, int64_t i) const {
1531 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1532 DisallowedType(context, GetIntegerString());
1533 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1534 }
1535
1536 if (!minimum_.IsNull()) {
1537 if (minimum_.IsInt64()) {
1538 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
1539 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1540 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1541 }
1542 }
1543 else if (minimum_.IsUint64()) {
1544 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1545 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
1546 }
1547 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1548 return false;
1549 }
1550
1551 if (!maximum_.IsNull()) {
1552 if (maximum_.IsInt64()) {
1553 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
1554 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1555 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1556 }
1557 }
1558 else if (maximum_.IsUint64()) { }
1559 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1560 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1561 return false;
1562 }
1563
1564 if (!multipleOf_.IsNull()) {
1565 if (multipleOf_.IsUint64()) {
1566 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
1567 context.error_handler.NotMultipleOf(i, multipleOf_);
1568 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1569 }
1570 }
1571 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1572 return false;
1573 }
1574
1575 return true;
1576 }
1577
1578 bool CheckUint(Context& context, uint64_t i) const {
1579 if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1580 DisallowedType(context, GetIntegerString());
1581 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1582 }
1583
1584 if (!minimum_.IsNull()) {
1585 if (minimum_.IsUint64()) {
1586 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
1587 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1588 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1589 }
1590 }
1591 else if (minimum_.IsInt64())
1592 /* do nothing */; // i >= 0 > minimum.Getint64()
1593 else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1594 return false;
1595 }
1596
1597 if (!maximum_.IsNull()) {
1598 if (maximum_.IsUint64()) {
1599 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
1600 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1601 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1602 }
1603 }
1604 else if (maximum_.IsInt64()) {
1605 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1606 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
1607 }
1608 else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1609 return false;
1610 }
1611
1612 if (!multipleOf_.IsNull()) {
1613 if (multipleOf_.IsUint64()) {
1614 if (i % multipleOf_.GetUint64() != 0) {
1615 context.error_handler.NotMultipleOf(i, multipleOf_);
1616 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1617 }
1618 }
1619 else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1620 return false;
1621 }
1622
1623 return true;
1624 }
1625
1626 bool CheckDoubleMinimum(Context& context, double d) const {
1627 if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
1628 context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
1629 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1630 }
1631 return true;
1632 }
1633
1634 bool CheckDoubleMaximum(Context& context, double d) const {
1635 if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
1636 context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
1637 RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1638 }
1639 return true;
1640 }
1641
1642 bool CheckDoubleMultipleOf(Context& context, double d) const {
1643 double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1644 double q = a / b;
1645 double qRounded = std::floor(q + 0.5);
1646 double scaledEpsilon = (q + qRounded) * std::numeric_limits<double>::epsilon();
1647 double difference = std::abs(qRounded - q);
1648 bool isMultiple = (difference <= scaledEpsilon)
1649 || (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>
1751struct TokenHelper {
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 if (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
1782///////////////////////////////////////////////////////////////////////////////
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
1803///////////////////////////////////////////////////////////////////////////////
1804// GenericSchemaDocument
1805
1806//! JSON schema document.
1807/*!
1808 A JSON schema document is a compiled version of a JSON schema.
1809 It is basically a tree of internal::Schema.
1810
1811 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1812 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1813 \tparam Allocator Allocator type for allocating memory of this document.
1814*/
1815template <typename ValueT, typename Allocator = CrtAllocator>
1817public:
1818 typedef ValueT ValueType;
1820 typedef Allocator AllocatorType;
1821 typedef typename ValueType::EncodingType EncodingType;
1822 typedef typename EncodingType::Ch Ch;
1823 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1828 friend class internal::Schema<GenericSchemaDocument>;
1829 template <typename, typename, typename>
1830 friend class GenericSchemaValidator;
1831
1832 //! Constructor.
1833 /*!
1834 Compile a JSON document into schema document.
1835
1836 \param document A JSON document as source.
1837 \param uri The base URI of this schema document for purposes of violation reporting.
1838 \param uriLength Length of \c name, in code points.
1839 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1840 \param allocator An optional allocator instance for allocating memory. Can be null.
1841 \param pointer An optional JSON pointer to the start of the schema document
1842 \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04.
1843 */
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
1896 //! Move constructor in C++11
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
1918 //! Destructor
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
1941 //! Static method to get the specification of any schema document
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
1955 //! Get the root schema.
1956 const SchemaType& GetRoot() const { return *root_; }
1957
1958 //! Gets the error object.
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
1981 //! Default error method
1982 void SchemaError(const SchemaErrorCode code, const PointerType& location) {
1983 currentError_ = GValue(kObjectType);
1984 AddCurrentError(code, location);
1985 }
1986
1987 //! Method for error with single string value insert
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
1994 //! Method for error with invalid pointer
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:
2003 //! Prohibit copying
2005 //! Prohibit assignment
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) {
2024 GenericStringBuffer<EncodingType> sb;
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())
2160 SchemaError(kSchemaErrorSpecUnsupported, PointerType());
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)
2220 SchemaError(kSchemaErrorRefInvalid, source);
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_)
2233 SchemaError(kSchemaErrorRefNoRemoteProvider, source);
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
2255 SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
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)
2293 SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
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
2321 //! Find the first subschema with a resolved 'id' that matches the specified URI.
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_; //!< Root schema.
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
2414//! GenericSchemaDocument using Value type.
2415typedef GenericSchemaDocument<Value> SchemaDocument;
2416//! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
2417typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
2418
2419///////////////////////////////////////////////////////////////////////////////
2420// GenericSchemaValidator
2421
2422//! JSON Schema Validator.
2423/*!
2424 A SAX style JSON schema validator.
2425 It uses a \c GenericSchemaDocument to validate SAX events.
2426 It delegates the incoming SAX events to an output handler.
2427 The default output handler does nothing.
2428 It can be reused multiple times by calling \c Reset().
2429
2430 \tparam SchemaDocumentType Type of schema document.
2431 \tparam OutputHandler Type of output handler. Default handler does nothing.
2432 \tparam StateAllocator Allocator for storing the internal validation states.
2433*/
2434template <
2435 typename SchemaDocumentType,
2436 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
2437 typename StateAllocator = CrtAllocator>
2439 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
2440 public internal::ISchemaValidator,
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;
2450
2451 //! Constructor without output handler.
2452 /*!
2453 \param schemaDocument The schema document to conform to.
2454 \param allocator Optional allocator for storing internal validation states.
2455 \param schemaStackCapacity Optional initial capacity of schema path stack.
2456 \param documentStackCapacity Optional initial capacity of document path stack.
2457 */
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
2481 //! Constructor with output handler.
2482 /*!
2483 \param schemaDocument The schema document to conform to.
2484 \param allocator Optional allocator for storing internal validation states.
2485 \param schemaStackCapacity Optional initial capacity of schema path stack.
2486 \param documentStackCapacity Optional initial capacity of document path stack.
2487 */
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
2512 //! Destructor.
2514 Reset();
2515 RAPIDJSON_DELETE(ownStateAllocator_);
2516 }
2517
2518 //! Reset the internal states.
2519 void Reset() {
2520 while (!schemaStack_.Empty())
2521 PopSchema();
2522 documentStack_.Clear();
2523 ResetError();
2524 }
2525
2526 //! Reset the error state.
2527 void ResetError() {
2528 error_.SetObject();
2529 currentError_.SetNull();
2530 missingDependents_.SetNull();
2531 valid_ = true;
2532 }
2533
2534 //! Implementation of ISchemaValidator
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 }
2547 //! End of Implementation of ISchemaValidator
2548
2549 //! Gets the error object.
2550 ValueType& GetError() { return error_; }
2551 const ValueType& GetError() const { return error_; }
2552
2553 //! Gets the JSON pointer pointed to the invalid schema.
2554 // If reporting all errors, the stack will be empty.
2555 PointerType GetInvalidSchemaPointer() const {
2556 return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
2557 }
2558
2559 //! Gets the keyword of invalid schema.
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
2567 //! Gets the error code of invalid schema.
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
2575 //! Gets the JSON pointer pointed to the invalid value.
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);
2911 v->~GenericSchemaValidator();
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
2943 GenericSchemaValidator(
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_;
3168 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
3169 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
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
3181///////////////////////////////////////////////////////////////////////////////
3182// SchemaValidatingReader
3183
3184//! A helper class for parsing with validation.
3185/*!
3186 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
3187
3188 \tparam parseFlags Combination of \ref ParseFlag.
3189 \tparam InputStream Type of input stream, implementing Stream concept.
3190 \tparam SourceEncoding Encoding of the input stream.
3191 \tparam SchemaDocumentType Type of schema document.
3192 \tparam StackAllocator Allocator type for stack.
3193*/
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
3206 //! Constructor
3207 /*!
3208 \param is Input stream.
3209 \param sd Schema document.
3210 */
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
3259RAPIDJSON_NAMESPACE_END
3260RAPIDJSON_DIAG_POP
3261
3262#endif // RAPIDJSON_SCHEMA_H_
Concept for allocating, resizing and freeing memory block.
Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
Definition pointer.h:68
size_t GetParseErrorOffset() const
Get the parsing error offset in code unit.
Definition pointer.h:330
SAX-style JSON parser. Use Reader for UTF8 encoding and default allocator.
Definition reader.h:539
JSON schema document.
Definition schema.h:1816
static const Specification GetSpecification(const ValueType &document)
Static method to get the specification of any schema document.
Definition schema.h:1943
const SchemaType & GetRoot() const
Get the root schema.
Definition schema.h:1956
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
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
void SchemaError(const SchemaErrorCode code, const PointerType &location)
Default error method.
Definition schema.h:1982
GValue & GetError()
Gets the error object.
Definition schema.h:1959
~GenericSchemaDocument()
Destructor.
Definition schema.h:1919
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
JSON Schema Validator.
Definition schema.h:2441
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
PointerType GetInvalidSchemaPointer() const
Gets the JSON pointer pointed to the invalid schema.
Definition schema.h:2555
PointerType GetInvalidDocumentPointer() const
Gets the JSON pointer pointed to the invalid value.
Definition schema.h:2577
const Ch * GetInvalidSchemaKeyword() const
Gets the keyword of invalid schema.
Definition schema.h:2561
ValueType & GetError()
End of Implementation of ISchemaValidator.
Definition schema.h:2550
void ResetError()
Reset the error state.
Definition schema.h:2527
void SetValidateFlags(unsigned flags)
Implementation of ISchemaValidator.
Definition schema.h:2535
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor without output handler.
Definition schema.h:2458
~GenericSchemaValidator()
Destructor.
Definition schema.h:2513
void Reset()
Reset the internal states.
Definition schema.h:2519
Represents an in-memory output stream.
Definition stringbuffer.h:41
size_t GetSize() const
Get the size of string in bytes in the string buffer.
Definition stringbuffer.h:82
Definition uri.h:33
Represents a JSON value. Use Value for UTF8 encoding and default allocator.
Definition fwd.h:114
Concept for receiving events from GenericReader upon parsing. The functions return true if no error o...
A helper class for parsing with validation.
Definition schema.h:3200
SchemaValidatingReader(InputStream &is, const SchemaDocumentType &sd)
Constructor.
Definition schema.h:3211
#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS
User-defined kValidateDefaultFlags definition.
Definition schema.h:174
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition rapidjson.h:437
ValidateErrorCode
Error codes when validating.
Definition error.h:162
SchemaErrorCode
Error codes when validating.
Definition error.h:220
@ kValidateErrors
Top level error code when kValidateContinueOnErrorsFlag set.
Definition error.h:163
@ kValidateErrorMaxItems
Array is longer than the 'maxItems' value.
Definition error.h:176
@ kValidateErrorRequired
Object is missing one or more members required by the schema.
Definition error.h:183
@ kValidateErrorMinProperties
Object has less members than 'minProperties' value.
Definition error.h:182
@ kValidateErrorMaximum
Number is greater than the 'maximum' value.
Definition error.h:167
@ kValidateErrorAdditionalProperties
Object has additional members that are not allowed by the schema.
Definition error.h:184
@ kValidateErrorNone
No error.
Definition error.h:164
@ kValidateErrorOneOf
Property did not match any of the sub-schemas specified by 'oneOf'.
Definition error.h:191
@ kValidateErrorEnum
Property has a value that is not one of its allowed enumerated values.
Definition error.h:188
@ kValidateErrorMaxLength
String is longer than the 'maxLength' value.
Definition error.h:172
@ kValidateErrorType
Property has a type that is not allowed by the schema.
Definition error.h:189
@ kValidateErrorMaxProperties
Object has more members than 'maxProperties' value.
Definition error.h:181
@ kValidateErrorNot
Property matched the sub-schema specified by 'not'.
Definition error.h:195
@ kValidateErrorExclusiveMinimum
Number is less than or equal to the 'minimum' value.
Definition error.h:170
@ kValidateErrorExclusiveMaximum
Number is greater than or equal to the 'maximum' value.
Definition error.h:168
@ kValidateErrorMultipleOf
Number is not a multiple of the 'multipleOf' value.
Definition error.h:166
@ kValidateErrorAnyOf
Property did not match any of the sub-schemas specified by 'anyOf'.
Definition error.h:194
@ kValidateErrorReadOnly
Property is read-only but has been provided when validation is for writing.
Definition error.h:197
@ kValidateErrorWriteOnly
Property is write-only but has been provided when validation is for reading.
Definition error.h:198
@ kValidateErrorAdditionalItems
Array has additional items that are not allowed by the schema.
Definition error.h:179
@ kValidateErrorPatternProperties
See other errors.
Definition error.h:185
@ kValidateErrorMinLength
String is longer than the 'maxLength' value.
Definition error.h:173
@ kValidateErrorMinimum
Number is less than the 'minimum' value.
Definition error.h:169
@ kValidateErrorDependencies
Object has missing property or schema dependencies.
Definition error.h:186
@ kValidateErrorMinItems
Array is shorter than the 'minItems' value.
Definition error.h:177
@ kValidateErrorOneOfMatch
Property matched more than one of the sub-schemas specified by 'oneOf'.
Definition error.h:192
@ kValidateErrorUniqueItems
Array has duplicate items but 'uniqueItems' is true.
Definition error.h:178
@ kValidateErrorPattern
String does not match the 'pattern' regular expression.
Definition error.h:174
@ kValidateErrorAllOf
Property did not match all of the sub-schemas specified by 'allOf'.
Definition error.h:193
@ kSchemaErrorStartUnknown
Pointer to start of schema does not resolve to a location in the document.
Definition error.h:223
unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition rapidjson.h:415
GenericSchemaDocument< Value, CrtAllocator > SchemaDocument
GenericSchemaDocument using Value type.
Definition fwd.h:138
OpenApiVersion
Definition schema.h:202
@ kVersionMin
Current minimum supported version.
Definition schema.h:205
@ kVersionMax
Current maximum supported version.
Definition schema.h:208
ValidateFlag
Combination of validate flags.
Definition schema.h:178
@ kValidateWriteFlag
Validation is for a write semantic.
Definition schema.h:182
@ kValidateContinueOnErrorFlag
Don't stop after first validation error.
Definition schema.h:180
@ kValidateNoFlags
No flags are set.
Definition schema.h:179
@ kValidateReadFlag
Validation is for a read semantic.
Definition schema.h:181
@ kValidateDefaultFlags
Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS.
Definition schema.h:183
SchemaDraft
Definition schema.h:188
@ kDraftMin
Current minimum supported draft.
Definition schema.h:192
@ kDraftMax
Current maximum supported draft.
Definition schema.h:195
@ kObjectType
object
Definition rapidjson.h:733
IGenericRemoteSchemaDocumentProvider< SchemaDocument > IRemoteSchemaDocumentProvider
IGenericRemoteSchemaDocumentProvider using SchemaDocument.
Definition fwd.h:139
#define RAPIDJSON_DELETE(x)
! customization point for global delete
Definition rapidjson.h:716
#define RAPIDJSON_UINT64_C2(high32, low32)
Construct a 64-bit literal by a pair of 32-bit integer.
Definition rapidjson.h:320
#define RAPIDJSON_NEW(TypeName)
! customization point for global new
Definition rapidjson.h:712
Reference to a constant string (not taking a copy)
Definition fwd.h:111
Definition schema.h:212