载入中...
搜索中...
未找到
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
184};
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
212struct Specification {
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 || difference < (std::numeric_limits<double>::min)();
1649 if (!isMultiple) {
1650 context.error_handler.NotMultipleOf(d, multipleOf_);
1651 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1652 }
1653 return true;
1654 }
1655
1656 void DisallowedType(Context& context, const ValueType& actualType) const {
1657 ErrorHandler& eh = context.error_handler;
1658 eh.StartDisallowedType();
1659
1660 if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
1661 if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
1662 if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
1663 if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
1664 if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
1665
1666 if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
1667 else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
1668
1669 eh.EndDisallowedType(actualType);
1670 }
1671
1672 struct Property {
1673 Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
1674 ~Property() { AllocatorType::Free(dependencies); }
1675 SValue name;
1676 const SchemaType* schema;
1677 const SchemaType* dependenciesSchema;
1678 SizeType dependenciesValidatorIndex;
1679 bool* dependencies;
1680 bool required;
1681 };
1682
1683 struct PatternProperty {
1684 PatternProperty() : schema(), pattern() {}
1685 ~PatternProperty() {
1686 if (pattern) {
1687 pattern->~RegexType();
1688 AllocatorType::Free(pattern);
1689 }
1690 }
1691 const SchemaType* schema;
1692 RegexType* pattern;
1693 };
1694
1695 AllocatorType* allocator_;
1696 SValue uri_;
1697 UriType id_;
1698 Specification spec_;
1699 PointerType pointer_;
1700 const SchemaType* typeless_;
1701 uint64_t* enum_;
1702 SizeType enumCount_;
1703 SchemaArray allOf_;
1704 SchemaArray anyOf_;
1705 SchemaArray oneOf_;
1706 const SchemaType* not_;
1707 unsigned type_; // bitmask of kSchemaType
1708 SizeType validatorCount_;
1709 SizeType notValidatorIndex_;
1710
1711 Property* properties_;
1712 const SchemaType* additionalPropertiesSchema_;
1713 PatternProperty* patternProperties_;
1714 SizeType patternPropertyCount_;
1715 SizeType propertyCount_;
1716 SizeType minProperties_;
1717 SizeType maxProperties_;
1718 bool additionalProperties_;
1719 bool hasDependencies_;
1720 bool hasRequired_;
1721 bool hasSchemaDependencies_;
1722
1723 const SchemaType* additionalItemsSchema_;
1724 const SchemaType* itemsList_;
1725 const SchemaType** itemsTuple_;
1726 SizeType itemsTupleCount_;
1727 SizeType minItems_;
1728 SizeType maxItems_;
1729 bool additionalItems_;
1730 bool uniqueItems_;
1731
1732 RegexType* pattern_;
1733 SizeType minLength_;
1734 SizeType maxLength_;
1735
1736 SValue minimum_;
1737 SValue maximum_;
1738 SValue multipleOf_;
1739 bool exclusiveMinimum_;
1740 bool exclusiveMaximum_;
1741
1742 SizeType defaultValueLength_;
1743
1744 bool readOnly_;
1745 bool writeOnly_;
1746 bool nullable_;
1747};
1748
1749template<typename Stack, typename Ch>
1750struct TokenHelper {
1751 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1752 *documentStack.template Push<Ch>() = '/';
1753 char buffer[21];
1754 size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1755 for (size_t i = 0; i < length; i++)
1756 *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1757 }
1758};
1759
1760// Partial specialized version for char to prevent buffer copying.
1761template <typename Stack>
1762struct TokenHelper<Stack, char> {
1763 RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1764 RAPIDJSON_IF_CONSTEXPR (sizeof(SizeType) == 4) {
1765 char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1766 *buffer++ = '/';
1767 const char* end = internal::u32toa(index, buffer);
1768 documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1769 }
1770 else {
1771 char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1772 *buffer++ = '/';
1773 const char* end = internal::u64toa(index, buffer);
1774 documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1775 }
1776 }
1777};
1778
1779} // namespace internal
1780
1781///////////////////////////////////////////////////////////////////////////////
1782// IGenericRemoteSchemaDocumentProvider
1783
1784template <typename SchemaDocumentType>
1786public:
1787 typedef typename SchemaDocumentType::Ch Ch;
1788 typedef typename SchemaDocumentType::ValueType ValueType;
1789 typedef typename SchemaDocumentType::AllocatorType AllocatorType;
1790
1792 virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1793 virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri<ValueType, AllocatorType> uri, Specification& spec) {
1794 // Default implementation just calls through for compatibility
1795 // Following line suppresses unused parameter warning
1796 (void)spec;
1797 // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi);
1798 return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength());
1799 }
1800};
1801
1802///////////////////////////////////////////////////////////////////////////////
1803// GenericSchemaDocument
1804
1805//! JSON schema document.
1806/*!
1807 A JSON schema document is a compiled version of a JSON schema.
1808 It is basically a tree of internal::Schema.
1809
1810 \note This is an immutable class (i.e. its instance cannot be modified after construction).
1811 \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1812 \tparam Allocator Allocator type for allocating memory of this document.
1813*/
1814template <typename ValueT, typename Allocator = CrtAllocator>
1816public:
1817 typedef ValueT ValueType;
1818 typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1819 typedef Allocator AllocatorType;
1820 typedef typename ValueType::EncodingType EncodingType;
1821 typedef typename EncodingType::Ch Ch;
1822 typedef internal::Schema<GenericSchemaDocument> SchemaType;
1823 typedef GenericPointer<ValueType, Allocator> PointerType;
1825 typedef GenericUri<ValueType, Allocator> UriType;
1826 typedef GenericStringRef<Ch> StringRefType;
1827 friend class internal::Schema<GenericSchemaDocument>;
1828 template <typename, typename, typename>
1829 friend class GenericSchemaValidator;
1830
1831 //! Constructor.
1832 /*!
1833 Compile a JSON document into schema document.
1834
1835 \param document A JSON document as source.
1836 \param uri The base URI of this schema document for purposes of violation reporting.
1837 \param uriLength Length of \c name, in code points.
1838 \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1839 \param allocator An optional allocator instance for allocating memory. Can be null.
1840 \param pointer An optional JSON pointer to the start of the schema document
1841 \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04.
1842 */
1843 explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
1844 IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
1845 const PointerType& pointer = PointerType(), // PR #1393
1846 const Specification& spec = Specification(kDraft04)) :
1847 remoteProvider_(remoteProvider),
1848 allocator_(allocator),
1849 ownAllocator_(),
1850 root_(),
1851 typeless_(),
1852 schemaMap_(allocator, kInitialSchemaMapSize),
1853 schemaRef_(allocator, kInitialSchemaRefSize),
1854 spec_(spec),
1855 error_(kObjectType),
1856 currentError_()
1857 {
1858 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument");
1859 if (!allocator_)
1860 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1861
1862 Ch noUri[1] = {0};
1863 uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
1864 docId_ = UriType(uri_, allocator_);
1865
1866 typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1867 new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
1868
1869 // Establish the schema draft or open api version.
1870 // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document.
1871 SetSchemaSpecification(document);
1872
1873 // Generate root schema, it will call CreateSchema() to create sub-schemas,
1874 // And call HandleRefSchema() if there are $ref.
1875 // PR #1393 use input pointer if supplied
1876 root_ = typeless_;
1877 if (pointer.GetTokenCount() == 0) {
1878 CreateSchemaRecursive(&root_, pointer, document, document, docId_);
1879 }
1880 else if (const ValueType* v = pointer.Get(document)) {
1881 CreateSchema(&root_, pointer, *v, document, docId_);
1882 }
1883 else {
1885 pointer.StringifyUriFragment(sb);
1886 SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)));
1887 }
1888
1889 RAPIDJSON_ASSERT(root_ != 0);
1890
1891 schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1892 }
1893
1894#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1895 //! Move constructor in C++11
1896 GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1897 remoteProvider_(rhs.remoteProvider_),
1898 allocator_(rhs.allocator_),
1899 ownAllocator_(rhs.ownAllocator_),
1900 root_(rhs.root_),
1901 typeless_(rhs.typeless_),
1902 schemaMap_(std::move(rhs.schemaMap_)),
1903 schemaRef_(std::move(rhs.schemaRef_)),
1904 uri_(std::move(rhs.uri_)),
1905 docId_(std::move(rhs.docId_)),
1906 spec_(rhs.spec_),
1907 error_(std::move(rhs.error_)),
1908 currentError_(std::move(rhs.currentError_))
1909 {
1910 rhs.remoteProvider_ = 0;
1911 rhs.allocator_ = 0;
1912 rhs.ownAllocator_ = 0;
1913 rhs.typeless_ = 0;
1914 }
1915#endif
1916
1917 //! Destructor
1919 while (!schemaMap_.Empty())
1920 schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1921
1922 if (typeless_) {
1923 typeless_->~SchemaType();
1924 Allocator::Free(typeless_);
1925 }
1926
1927 // these may contain some allocator data so clear before deleting ownAllocator_
1928 uri_.SetNull();
1929 error_.SetNull();
1930 currentError_.SetNull();
1931
1932 RAPIDJSON_DELETE(ownAllocator_);
1933 }
1934
1935 const GValue& GetURI() const { return uri_; }
1936
1937 const Specification& GetSpecification() const { return spec_; }
1938 bool IsSupportedSpecification() const { return spec_.IsSupported(); }
1939
1940 //! Static method to get the specification of any schema document
1941 // Returns kDraftNone if document is silent
1942 static const Specification GetSpecification(const ValueType& document) {
1943 SchemaDraft draft = GetSchemaDraft(document);
1944 if (draft != kDraftNone)
1945 return Specification(draft);
1946 else {
1947 OpenApiVersion oapi = GetOpenApiVersion(document);
1948 if (oapi != kVersionNone)
1949 return Specification(oapi);
1950 }
1951 return Specification(kDraftNone);
1952 }
1953
1954 //! Get the root schema.
1955 const SchemaType& GetRoot() const { return *root_; }
1956
1957 //! Gets the error object.
1958 GValue& GetError() { return error_; }
1959 const GValue& GetError() const { return error_; }
1960
1961 static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) {
1962 switch (schemaErrorCode) {
1963 case kSchemaErrorStartUnknown: return GetStartUnknownString();
1964 case kSchemaErrorRefPlainName: return GetRefPlainNameString();
1965 case kSchemaErrorRefInvalid: return GetRefInvalidString();
1966 case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString();
1967 case kSchemaErrorRefUnknown: return GetRefUnknownString();
1968 case kSchemaErrorRefCyclical: return GetRefCyclicalString();
1969 case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString();
1970 case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString();
1971 case kSchemaErrorRegexInvalid: return GetRegexInvalidString();
1972 case kSchemaErrorSpecUnknown: return GetSpecUnknownString();
1973 case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString();
1974 case kSchemaErrorSpecIllegal: return GetSpecIllegalString();
1975 case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString();
1976 default: return GetNullString();
1977 }
1978 }
1979
1980 //! Default error method
1981 void SchemaError(const SchemaErrorCode code, const PointerType& location) {
1982 currentError_ = GValue(kObjectType);
1983 AddCurrentError(code, location);
1984 }
1985
1986 //! Method for error with single string value insert
1987 void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) {
1988 currentError_ = GValue(kObjectType);
1989 currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
1990 AddCurrentError(code, location);
1991 }
1992
1993 //! Method for error with invalid pointer
1994 void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) {
1995 currentError_ = GValue(kObjectType);
1996 currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
1997 currentError_.AddMember(GetOffsetString(), static_cast<SizeType>(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_);
1998 AddCurrentError(code, location);
1999 }
2000
2001 private:
2002 //! Prohibit copying
2004 //! Prohibit assignment
2006
2007 typedef const PointerType* SchemaRefPtr; // PR #1393
2008
2009 struct SchemaEntry {
2010 SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
2011 ~SchemaEntry() {
2012 if (owned) {
2013 schema->~SchemaType();
2014 Allocator::Free(schema);
2015 }
2016 }
2017 PointerType pointer;
2018 SchemaType* schema;
2019 bool owned;
2020 };
2021
2022 void AddErrorInstanceLocation(GValue& result, const PointerType& location) {
2023 GenericStringBuffer<EncodingType> sb;
2024 location.StringifyUriFragment(sb);
2025 GValue instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)), *allocator_);
2026 result.AddMember(GetInstanceRefString(), instanceRef, *allocator_);
2027 }
2028
2029 void AddError(GValue& keyword, GValue& error) {
2030 typename GValue::MemberIterator member = error_.FindMember(keyword);
2031 if (member == error_.MemberEnd())
2032 error_.AddMember(keyword, error, *allocator_);
2033 else {
2034 if (member->value.IsObject()) {
2035 GValue errors(kArrayType);
2036 errors.PushBack(member->value, *allocator_);
2037 member->value = errors;
2038 }
2039 member->value.PushBack(error, *allocator_);
2040 }
2041 }
2042
2043 void AddCurrentError(const SchemaErrorCode code, const PointerType& location) {
2044 RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code));
2045 currentError_.AddMember(GetErrorCodeString(), code, *allocator_);
2046 AddErrorInstanceLocation(currentError_, location);
2047 AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_);
2048 }
2049
2050#define RAPIDJSON_STRING_(name, ...) \
2051 static const StringRefType& Get##name##String() {\
2052 static const Ch s[] = { __VA_ARGS__, '\0' };\
2053 static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
2054 return v;\
2055 }
2056
2057 RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
2058 RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
2059 RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e')
2060 RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't')
2061
2062 RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
2063 RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
2064 RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd')
2065 RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l')
2066 RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
2067 RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e')
2068 RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
2069 RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
2070 RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
2071 RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l')
2072 RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r')
2073 RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a')
2074 RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
2075 RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
2076
2077#undef RAPIDJSON_STRING_
2078
2079 // Static method to get schema draft of any schema document
2080 static SchemaDraft GetSchemaDraft(const ValueType& document) {
2081 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' };
2082 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' };
2083 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' };
2084 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' };
2085 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' };
2086 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' };
2087 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' };
2088
2089 if (!document.IsObject()) {
2090 return kDraftNone;
2091 }
2092
2093 // Get the schema draft from the $schema keyword at the supplied location
2094 typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString());
2095 if (itr != document.MemberEnd()) {
2096 if (!itr->value.IsString()) return kDraftUnknown;
2097 const UriType draftUri(itr->value);
2098 // Check base uri for match
2099 if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04;
2100 if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05;
2101 if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06;
2102 if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07;
2103 if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03;
2104 if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09;
2105 if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12;
2106 return kDraftUnknown;
2107 }
2108 // $schema not found
2109 return kDraftNone;
2110 }
2111
2112
2113 // Get open api version of any schema document
2114 static OpenApiVersion GetOpenApiVersion(const ValueType& document) {
2115 static const Ch kVersion20String[] = { '2', '.', '0', '\0' };
2116 static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level
2117 static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level
2118 static SizeType len = internal::StrLen<Ch>(kVersion30String);
2119
2120 if (!document.IsObject()) {
2121 return kVersionNone;
2122 }
2123
2124 // Get the open api version from the swagger / openapi keyword at the supplied location
2125 typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString());
2126 if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString());
2127 if (itr != document.MemberEnd()) {
2128 if (!itr->value.IsString()) return kVersionUnknown;
2129 const ValueType kVersion20Value(kVersion20String);
2130 if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly
2131 const ValueType kVersion30Value(kVersion30String);
2132 if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x
2133 const ValueType kVersion31Value(kVersion31String);
2134 if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x
2135 return kVersionUnknown;
2136 }
2137 // swagger or openapi not found
2138 return kVersionNone;
2139 }
2140
2141 // Get the draft of the schema or the open api version (which implies the draft).
2142 // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on.
2143 void SetSchemaSpecification(const ValueType& document) {
2144 // Look for '$schema', 'swagger' or 'openapi' keyword at document root
2145 SchemaDraft docDraft = GetSchemaDraft(document);
2146 OpenApiVersion docOapi = GetOpenApiVersion(document);
2147 // Error if both in document
2148 if (docDraft != kDraftNone && docOapi != kVersionNone)
2149 SchemaError(kSchemaErrorSpecIllegal, PointerType());
2150 // Use document draft or open api version if present or use spec from constructor
2151 if (docDraft != kDraftNone)
2152 spec_ = Specification(docDraft);
2153 else if (docOapi != kVersionNone)
2154 spec_ = Specification(docOapi);
2155 // Error if draft or version unknown
2156 if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown)
2157 SchemaError(kSchemaErrorSpecUnknown, PointerType());
2158 else if (!spec_.IsSupported())
2159 SchemaError(kSchemaErrorSpecUnsupported, PointerType());
2160 }
2161
2162 // Changed by PR #1393
2163 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
2164 if (v.GetType() == kObjectType) {
2165 UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_);
2166
2167 for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
2168 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
2169 }
2170 else if (v.GetType() == kArrayType)
2171 for (SizeType i = 0; i < v.Size(); i++)
2172 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id);
2173 }
2174
2175 // Changed by PR #1393
2176 const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
2177 RAPIDJSON_ASSERT(pointer.IsValid());
2178 GenericStringBuffer<EncodingType> sb;
2179 pointer.StringifyUriFragment(sb);
2180 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString());
2181 if (v.IsObject()) {
2182 if (const SchemaType* sc = GetSchema(pointer)) {
2183 if (schema)
2184 *schema = sc;
2185 AddSchemaRefs(const_cast<SchemaType*>(sc));
2186 }
2187 else if (!HandleRefSchema(pointer, schema, v, document, id)) {
2188 // The new schema constructor adds itself and its $ref(s) to schemaMap_
2189 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
2190 if (schema)
2191 *schema = s;
2192 return s->GetId();
2193 }
2194 }
2195 else {
2196 if (schema)
2197 *schema = typeless_;
2198 AddSchemaRefs(typeless_);
2199 }
2200 return id;
2201 }
2202
2203 // Changed by PR #1393
2204 // TODO should this return a UriType& ?
2205 bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
2206 typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
2207 if (itr == v.MemberEnd())
2208 return false;
2209
2210 GenericStringBuffer<EncodingType> sb;
2211 source.StringifyUriFragment(sb);
2212 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString());
2213 // Resolve the source pointer to the $ref'ed schema (finally)
2214 new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
2215
2216 if (itr->value.IsString()) {
2217 SizeType len = itr->value.GetStringLength();
2218 if (len == 0)
2219 SchemaError(kSchemaErrorRefInvalid, source);
2220 else {
2221 // First resolve $ref against the in-scope id
2222 UriType scopeId = UriType(id, allocator_);
2223 UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
2224 RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString());
2225 // See if the resolved $ref minus the fragment matches a resolved id in this document
2226 // Search from the root. Returns the subschema in the document and its absolute JSON pointer.
2227 PointerType basePointer = PointerType();
2228 const ValueType *base = FindId(document, ref, basePointer, docId_, false);
2229 if (!base) {
2230 // Remote reference - call the remote document provider
2231 if (!remoteProvider_)
2232 SchemaError(kSchemaErrorRefNoRemoteProvider, source);
2233 else {
2234 if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) {
2235 const Ch* s = ref.GetFragString();
2236 len = ref.GetFragStringLength();
2237 if (len <= 1 || s[1] == '/') {
2238 // JSON pointer fragment, absolute in the remote schema
2239 const PointerType pointer(s, len, allocator_);
2240 if (!pointer.IsValid())
2241 SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer);
2242 else {
2243 // Get the subschema
2244 if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
2245 if (schema)
2246 *schema = sc;
2247 AddSchemaRefs(const_cast<SchemaType *>(sc));
2248 return true;
2249 } else
2250 SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
2251 }
2252 } else
2253 // Plain name fragment, not allowed in remote schema
2254 SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
2255 } else
2256 SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength());
2257 }
2258 }
2259 else { // Local reference
2260 const Ch* s = ref.GetFragString();
2261 len = ref.GetFragStringLength();
2262 if (len <= 1 || s[1] == '/') {
2263 // JSON pointer fragment, relative to the resolved URI
2264 const PointerType relPointer(s, len, allocator_);
2265 if (!relPointer.IsValid())
2266 SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer);
2267 else {
2268 // Get the subschema
2269 if (const ValueType *pv = relPointer.Get(*base)) {
2270 // Now get the absolute JSON pointer by adding relative to base
2271 PointerType pointer(basePointer, allocator_);
2272 for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
2273 pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
2274 if (IsCyclicRef(pointer))
2275 SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
2276 else {
2277 // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
2278 // TODO: cache pointer <-> id mapping
2279 size_t unresolvedTokenIndex;
2280 scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
2281 CreateSchema(schema, pointer, *pv, document, scopeId);
2282 return true;
2283 }
2284 } else
2285 SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
2286 }
2287 } else {
2288 // Plain name fragment, relative to the resolved URI
2289 // Not supported in open api 2.0 and 3.0
2290 PointerType pointer(allocator_);
2291 if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30)
2292 SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
2293 // See if the fragment matches an id in this document.
2294 // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
2295 else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
2296 if (IsCyclicRef(pointer))
2297 SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
2298 else {
2299 // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
2300 // TODO: cache pointer <-> id mapping
2301 size_t unresolvedTokenIndex;
2302 scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
2303 CreateSchema(schema, pointer, *pv, document, scopeId);
2304 return true;
2305 }
2306 } else
2307 SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
2308 }
2309 }
2310 }
2311 }
2312
2313 // Invalid/Unknown $ref
2314 if (schema)
2315 *schema = typeless_;
2316 AddSchemaRefs(typeless_);
2317 return true;
2318 }
2319
2320 //! Find the first subschema with a resolved 'id' that matches the specified URI.
2321 // If full specified use all URI else ignore fragment.
2322 // If found, return a pointer to the subschema and its JSON pointer.
2323 // TODO cache pointer <-> id mapping
2324 ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const {
2325 SizeType i = 0;
2326 ValueType* resval = 0;
2327 UriType tempuri = UriType(finduri, allocator_);
2328 UriType localuri = UriType(baseuri, allocator_);
2329 if (doc.GetType() == kObjectType) {
2330 // Establish the base URI of this object
2331 typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
2332 if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
2333 localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
2334 }
2335 // See if it matches
2336 if (localuri.Match(finduri, full)) {
2337 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString());
2338 resval = const_cast<ValueType *>(&doc);
2339 resptr = here;
2340 return resval;
2341 }
2342 // No match, continue looking
2343 for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
2344 if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
2345 resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
2346 }
2347 if (resval) break;
2348 }
2349 } else if (doc.GetType() == kArrayType) {
2350 // Continue looking
2351 for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
2352 if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
2353 resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
2354 }
2355 if (resval) break;
2356 i++;
2357 }
2358 }
2359 return resval;
2360 }
2361
2362 // Added by PR #1393
2363 void AddSchemaRefs(SchemaType* schema) {
2364 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs");
2365 while (!schemaRef_.Empty()) {
2366 SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
2367 SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
2368 new (entry) SchemaEntry(**ref, schema, false, allocator_);
2369 }
2370 }
2371
2372 // Added by PR #1393
2373 bool IsCyclicRef(const PointerType& pointer) const {
2374 for (const SchemaRefPtr* ref = schemaRef_.template Bottom<SchemaRefPtr>(); ref != schemaRef_.template End<SchemaRefPtr>(); ++ref)
2375 if (pointer == **ref)
2376 return true;
2377 return false;
2378 }
2379
2380 const SchemaType* GetSchema(const PointerType& pointer) const {
2381 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
2382 if (pointer == target->pointer)
2383 return target->schema;
2384 return 0;
2385 }
2386
2387 PointerType GetPointer(const SchemaType* schema) const {
2388 for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
2389 if (schema == target->schema)
2390 return target->pointer;
2391 return PointerType();
2392 }
2393
2394 const SchemaType* GetTypeless() const { return typeless_; }
2395
2396 static const size_t kInitialSchemaMapSize = 64;
2397 static const size_t kInitialSchemaRefSize = 64;
2398
2399 IRemoteSchemaDocumentProviderType* remoteProvider_;
2400 Allocator *allocator_;
2401 Allocator *ownAllocator_;
2402 const SchemaType* root_; //!< Root schema.
2403 SchemaType* typeless_;
2404 internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
2405 internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
2406 GValue uri_; // Schema document URI
2407 UriType docId_;
2408 Specification spec_;
2409 GValue error_;
2410 GValue currentError_;
2411};
2412
2413//! GenericSchemaDocument using Value type.
2414typedef GenericSchemaDocument<Value> SchemaDocument;
2415//! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
2416typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
2417
2418///////////////////////////////////////////////////////////////////////////////
2419// GenericSchemaValidator
2420
2421//! JSON Schema Validator.
2422/*!
2423 A SAX style JSON schema validator.
2424 It uses a \c GenericSchemaDocument to validate SAX events.
2425 It delegates the incoming SAX events to an output handler.
2426 The default output handler does nothing.
2427 It can be reused multiple times by calling \c Reset().
2428
2429 \tparam SchemaDocumentType Type of schema document.
2430 \tparam OutputHandler Type of output handler. Default handler does nothing.
2431 \tparam StateAllocator Allocator for storing the internal validation states.
2432*/
2433template <
2434 typename SchemaDocumentType,
2435 typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
2436 typename StateAllocator = CrtAllocator>
2438 public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
2439 public internal::ISchemaValidator,
2440 public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
2441public:
2442 typedef typename SchemaDocumentType::SchemaType SchemaType;
2443 typedef typename SchemaDocumentType::PointerType PointerType;
2444 typedef typename SchemaType::EncodingType EncodingType;
2445 typedef typename SchemaType::SValue SValue;
2446 typedef typename EncodingType::Ch Ch;
2447 typedef GenericStringRef<Ch> StringRefType;
2449
2450 //! Constructor without output handler.
2451 /*!
2452 \param schemaDocument The schema document to conform to.
2453 \param allocator Optional allocator for storing internal validation states.
2454 \param schemaStackCapacity Optional initial capacity of schema path stack.
2455 \param documentStackCapacity Optional initial capacity of document path stack.
2456 */
2458 const SchemaDocumentType& schemaDocument,
2459 StateAllocator* allocator = 0,
2460 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2461 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2462 :
2463 schemaDocument_(&schemaDocument),
2464 root_(schemaDocument.GetRoot()),
2465 stateAllocator_(allocator),
2466 ownStateAllocator_(0),
2467 schemaStack_(allocator, schemaStackCapacity),
2468 documentStack_(allocator, documentStackCapacity),
2469 outputHandler_(0),
2470 error_(kObjectType),
2471 currentError_(),
2472 missingDependents_(),
2473 valid_(true),
2474 flags_(kValidateDefaultFlags),
2475 depth_(0)
2476 {
2477 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator");
2478 }
2479
2480 //! Constructor with output handler.
2481 /*!
2482 \param schemaDocument The schema document to conform to.
2483 \param allocator Optional allocator for storing internal validation states.
2484 \param schemaStackCapacity Optional initial capacity of schema path stack.
2485 \param documentStackCapacity Optional initial capacity of document path stack.
2486 */
2488 const SchemaDocumentType& schemaDocument,
2489 OutputHandler& outputHandler,
2490 StateAllocator* allocator = 0,
2491 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2492 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2493 :
2494 schemaDocument_(&schemaDocument),
2495 root_(schemaDocument.GetRoot()),
2496 stateAllocator_(allocator),
2497 ownStateAllocator_(0),
2498 schemaStack_(allocator, schemaStackCapacity),
2499 documentStack_(allocator, documentStackCapacity),
2500 outputHandler_(&outputHandler),
2501 error_(kObjectType),
2502 currentError_(),
2503 missingDependents_(),
2504 valid_(true),
2505 flags_(kValidateDefaultFlags),
2506 depth_(0)
2507 {
2508 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)");
2509 }
2510
2511 //! Destructor.
2513 Reset();
2514 RAPIDJSON_DELETE(ownStateAllocator_);
2515 }
2516
2517 //! Reset the internal states.
2518 void Reset() {
2519 while (!schemaStack_.Empty())
2520 PopSchema();
2521 documentStack_.Clear();
2522 ResetError();
2523 }
2524
2525 //! Reset the error state.
2526 void ResetError() {
2527 error_.SetObject();
2528 currentError_.SetNull();
2529 missingDependents_.SetNull();
2530 valid_ = true;
2531 }
2532
2533 //! Implementation of ISchemaValidator
2534 void SetValidateFlags(unsigned flags) {
2535 flags_ = flags;
2536 }
2537 virtual unsigned GetValidateFlags() const {
2538 return flags_;
2539 }
2540
2541 virtual bool IsValid() const {
2542 if (!valid_) return false;
2543 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
2544 return true;
2545 }
2546 //! End of Implementation of ISchemaValidator
2547
2548 //! Gets the error object.
2549 ValueType& GetError() { return error_; }
2550 const ValueType& GetError() const { return error_; }
2551
2552 //! Gets the JSON pointer pointed to the invalid schema.
2553 // If reporting all errors, the stack will be empty.
2554 PointerType GetInvalidSchemaPointer() const {
2555 return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
2556 }
2557
2558 //! Gets the keyword of invalid schema.
2559 // If reporting all errors, the stack will be empty, so return "errors".
2560 const Ch* GetInvalidSchemaKeyword() const {
2561 if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
2562 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return static_cast<const Ch*>(GetErrorsString());
2563 return 0;
2564 }
2565
2566 //! Gets the error code of invalid schema.
2567 // If reporting all errors, the stack will be empty, so return kValidateErrors.
2569 if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
2570 if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
2571 return kValidateErrorNone;
2572 }
2573
2574 //! Gets the JSON pointer pointed to the invalid value.
2575 // If reporting all errors, the stack will be empty.
2576 PointerType GetInvalidDocumentPointer() const {
2577 if (documentStack_.Empty()) {
2578 return PointerType();
2579 }
2580 else {
2581 return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
2582 }
2583 }
2584
2585 void NotMultipleOf(int64_t actual, const SValue& expected) {
2586 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
2587 }
2588 void NotMultipleOf(uint64_t actual, const SValue& expected) {
2589 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
2590 }
2591 void NotMultipleOf(double actual, const SValue& expected) {
2592 AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
2593 }
2594 void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
2595 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2596 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2597 }
2598 void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
2599 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2600 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2601 }
2602 void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
2603 AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
2604 exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
2605 }
2606 void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
2607 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2608 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2609 }
2610 void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
2611 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2612 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2613 }
2614 void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
2615 AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
2616 exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
2617 }
2618
2619 void TooLong(const Ch* str, SizeType length, SizeType expected) {
2620 AddNumberError(kValidateErrorMaxLength,
2621 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
2622 }
2623 void TooShort(const Ch* str, SizeType length, SizeType expected) {
2624 AddNumberError(kValidateErrorMinLength,
2625 ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
2626 }
2627 void DoesNotMatch(const Ch* str, SizeType length) {
2628 currentError_.SetObject();
2629 currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
2630 AddCurrentError(kValidateErrorPattern);
2631 }
2632
2633 void DisallowedItem(SizeType index) {
2634 currentError_.SetObject();
2635 currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
2636 AddCurrentError(kValidateErrorAdditionalItems, true);
2637 }
2638 void TooFewItems(SizeType actualCount, SizeType expectedCount) {
2639 AddNumberError(kValidateErrorMinItems,
2640 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2641 }
2642 void TooManyItems(SizeType actualCount, SizeType expectedCount) {
2643 AddNumberError(kValidateErrorMaxItems,
2644 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2645 }
2646 void DuplicateItems(SizeType index1, SizeType index2) {
2647 ValueType duplicates(kArrayType);
2648 duplicates.PushBack(index1, GetStateAllocator());
2649 duplicates.PushBack(index2, GetStateAllocator());
2650 currentError_.SetObject();
2651 currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
2652 AddCurrentError(kValidateErrorUniqueItems, true);
2653 }
2654
2655 void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
2656 AddNumberError(kValidateErrorMaxProperties,
2657 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2658 }
2659 void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
2660 AddNumberError(kValidateErrorMinProperties,
2661 ValueType(actualCount).Move(), SValue(expectedCount).Move());
2662 }
2663 void StartMissingProperties() {
2664 currentError_.SetArray();
2665 }
2666 void AddMissingProperty(const SValue& name) {
2667 currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
2668 }
2669 bool EndMissingProperties() {
2670 if (currentError_.Empty())
2671 return false;
2672 ValueType error(kObjectType);
2673 error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
2674 currentError_ = error;
2675 AddCurrentError(kValidateErrorRequired);
2676 return true;
2677 }
2678 void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
2679 for (SizeType i = 0; i < count; ++i)
2680 MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2681 }
2682 void DisallowedProperty(const Ch* name, SizeType length) {
2683 currentError_.SetObject();
2684 currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
2685 AddCurrentError(kValidateErrorAdditionalProperties, true);
2686 }
2687
2688 void StartDependencyErrors() {
2689 currentError_.SetObject();
2690 }
2691 void StartMissingDependentProperties() {
2692 missingDependents_.SetArray();
2693 }
2694 void AddMissingDependentProperty(const SValue& targetName) {
2695 missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
2696 }
2697 void EndMissingDependentProperties(const SValue& sourceName) {
2698 if (!missingDependents_.Empty()) {
2699 // Create equivalent 'required' error
2700 ValueType error(kObjectType);
2702 error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
2703 AddErrorCode(error, code);
2704 AddErrorInstanceLocation(error, false);
2705 // When appending to a pointer ensure its allocator is used
2706 PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
2707 AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
2708 ValueType wrapper(kObjectType);
2709 wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
2710 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
2711 }
2712 }
2713 void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
2714 currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
2715 static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
2716 }
2717 bool EndDependencyErrors() {
2718 if (currentError_.ObjectEmpty())
2719 return false;
2720 ValueType error(kObjectType);
2721 error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
2722 currentError_ = error;
2723 AddCurrentError(kValidateErrorDependencies);
2724 return true;
2725 }
2726
2727 void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
2728 currentError_.SetObject();
2729 AddCurrentError(code);
2730 }
2731 void StartDisallowedType() {
2732 currentError_.SetArray();
2733 }
2734 void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
2735 currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
2736 }
2737 void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
2738 ValueType error(kObjectType);
2739 error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
2740 error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
2741 currentError_ = error;
2742 AddCurrentError(kValidateErrorType);
2743 }
2744 void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
2745 // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
2746 AddErrorArray(kValidateErrorAllOf, subvalidators, count);
2747 //for (SizeType i = 0; i < count; ++i) {
2748 // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2749 //}
2750 }
2751 void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
2752 AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
2753 }
2754 void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
2755 AddErrorArray(kValidateErrorOneOf, subvalidators, count);
2756 }
2757 void MultipleOneOf(SizeType index1, SizeType index2) {
2758 ValueType matches(kArrayType);
2759 matches.PushBack(index1, GetStateAllocator());
2760 matches.PushBack(index2, GetStateAllocator());
2761 currentError_.SetObject();
2762 currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator());
2763 AddCurrentError(kValidateErrorOneOfMatch);
2764 }
2765 void Disallowed() {
2766 currentError_.SetObject();
2767 AddCurrentError(kValidateErrorNot);
2768 }
2769 void DisallowedWhenWriting() {
2770 currentError_.SetObject();
2771 AddCurrentError(kValidateErrorReadOnly);
2772 }
2773 void DisallowedWhenReading() {
2774 currentError_.SetObject();
2775 AddCurrentError(kValidateErrorWriteOnly);
2776 }
2777
2778#define RAPIDJSON_STRING_(name, ...) \
2779 static const StringRefType& Get##name##String() {\
2780 static const Ch s[] = { __VA_ARGS__, '\0' };\
2781 static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
2782 return v;\
2783 }
2784
2785 RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
2786 RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
2787 RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
2788 RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
2789 RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
2790 RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
2791 RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
2792 RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
2793 RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
2794 RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
2795 RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's')
2796
2797#undef RAPIDJSON_STRING_
2798
2799#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
2800 if (!valid_) return false; \
2801 if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
2802 *documentStack_.template Push<Ch>() = '\0';\
2803 documentStack_.template Pop<Ch>(1);\
2804 RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom<Ch>());\
2805 valid_ = false;\
2806 return valid_;\
2807 }
2808
2809#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
2810 for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
2811 if (context->hasher)\
2812 static_cast<HasherType*>(context->hasher)->method arg2;\
2813 if (context->validators)\
2814 for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
2815 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
2816 if (context->patternPropertiesValidators)\
2817 for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
2818 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
2819 }
2820
2821#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
2822 valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
2823 return valid_;
2824
2825#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
2826 RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
2827 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
2828 RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
2829
2830 bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); }
2831 bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
2832 bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
2833 bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
2834 bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
2835 bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
2836 bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
2837 bool RawNumber(const Ch* str, SizeType length, bool copy)
2838 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2839 bool String(const Ch* str, SizeType length, bool copy)
2840 { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
2841
2842 bool StartObject() {
2843 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject");
2844 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
2845 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
2846 valid_ = !outputHandler_ || outputHandler_->StartObject();
2847 return valid_;
2848 }
2849
2850 bool Key(const Ch* str, SizeType len, bool copy) {
2851 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str);
2852 if (!valid_) return false;
2853 AppendToken(str, len);
2854 if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) {
2855 valid_ = false;
2856 return valid_;
2857 }
2858 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
2859 valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
2860 return valid_;
2861 }
2862
2863 bool EndObject(SizeType memberCount) {
2864 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject");
2865 if (!valid_) return false;
2866 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
2867 if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) {
2868 valid_ = false;
2869 return valid_;
2870 }
2871 RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
2872 }
2873
2874 bool StartArray() {
2875 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray");
2876 RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
2877 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
2878 valid_ = !outputHandler_ || outputHandler_->StartArray();
2879 return valid_;
2880 }
2881
2882 bool EndArray(SizeType elementCount) {
2883 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray");
2884 if (!valid_) return false;
2885 RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
2886 if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) {
2887 valid_ = false;
2888 return valid_;
2889 }
2890 RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
2891 }
2892
2893#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
2894#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
2895#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
2896
2897 // Implementation of ISchemaStateFactory<SchemaType>
2898 virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
2899 *documentStack_.template Push<Ch>() = '\0';
2900 documentStack_.template Pop<Ch>(1);
2901 ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
2902 depth_ + 1,
2903 &GetStateAllocator());
2904 sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~static_cast<unsigned>(kValidateContinueOnErrorFlag));
2905 return sv;
2906 }
2907
2908 virtual void DestroySchemaValidator(ISchemaValidator* validator) {
2909 GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
2910 v->~GenericSchemaValidator();
2911 StateAllocator::Free(v);
2912 }
2913
2914 virtual void* CreateHasher() {
2915 return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
2916 }
2917
2918 virtual uint64_t GetHashCode(void* hasher) {
2919 return static_cast<HasherType*>(hasher)->GetHashCode();
2920 }
2921
2922 virtual void DestroryHasher(void* hasher) {
2923 HasherType* h = static_cast<HasherType*>(hasher);
2924 h->~HasherType();
2925 StateAllocator::Free(h);
2926 }
2927
2928 virtual void* MallocState(size_t size) {
2929 return GetStateAllocator().Malloc(size);
2930 }
2931
2932 virtual void FreeState(void* p) {
2933 StateAllocator::Free(p);
2934 }
2935 // End of implementation of ISchemaStateFactory<SchemaType>
2936
2937private:
2938 typedef typename SchemaType::Context Context;
2939 typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
2940 typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
2941
2942 GenericSchemaValidator(
2943 const SchemaDocumentType& schemaDocument,
2944 const SchemaType& root,
2945 const char* basePath, size_t basePathSize,
2946 unsigned depth,
2947 StateAllocator* allocator = 0,
2948 size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
2949 size_t documentStackCapacity = kDefaultDocumentStackCapacity)
2950 :
2951 schemaDocument_(&schemaDocument),
2952 root_(root),
2953 stateAllocator_(allocator),
2954 ownStateAllocator_(0),
2955 schemaStack_(allocator, schemaStackCapacity),
2956 documentStack_(allocator, documentStackCapacity),
2957 outputHandler_(0),
2958 error_(kObjectType),
2959 currentError_(),
2960 missingDependents_(),
2961 valid_(true),
2962 flags_(kValidateDefaultFlags),
2963 depth_(depth)
2964 {
2965 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : "");
2966 if (basePath && basePathSize)
2967 memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
2968 }
2969
2970 StateAllocator& GetStateAllocator() {
2971 if (!stateAllocator_)
2972 stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
2973 return *stateAllocator_;
2974 }
2975
2976 bool GetContinueOnErrors() const {
2977 return flags_ & kValidateContinueOnErrorFlag;
2978 }
2979
2980 bool BeginValue() {
2981 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue");
2982 if (schemaStack_.Empty())
2983 PushSchema(root_);
2984 else {
2985 if (CurrentContext().inArray)
2986 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
2987
2988 if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
2989 return false;
2990
2991 SizeType count = CurrentContext().patternPropertiesSchemaCount;
2992 const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
2993 typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
2994 bool valueUniqueness = CurrentContext().valueUniqueness;
2995 RAPIDJSON_ASSERT(CurrentContext().valueSchema);
2996 PushSchema(*CurrentContext().valueSchema);
2997
2998 if (count > 0) {
2999 CurrentContext().objectPatternValidatorType = patternValidatorType;
3000 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
3001 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
3002 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
3003 std::memset(va, 0, sizeof(ISchemaValidator*) * count);
3004 for (SizeType i = 0; i < count; i++)
3005 va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
3006 }
3007
3008 CurrentContext().arrayUniqueness = valueUniqueness;
3009 }
3010 return true;
3011 }
3012
3013 bool EndValue() {
3014 RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue");
3015 if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
3016 return false;
3017
3018 GenericStringBuffer<EncodingType> sb;
3019 schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb);
3020 *documentStack_.template Push<Ch>() = '\0';
3021 documentStack_.template Pop<Ch>(1);
3022 RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom<Ch>(), depth_);
3023 void* hasher = CurrentContext().hasher;
3024 uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
3025
3026 PopSchema();
3027
3028 if (!schemaStack_.Empty()) {
3029 Context& context = CurrentContext();
3030 // Only check uniqueness if there is a hasher
3031 if (hasher && context.valueUniqueness) {
3032 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
3033 if (!a)
3034 CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
3035 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
3036 if (itr->GetUint64() == h) {
3037 DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
3038 // Cleanup before returning if continuing
3039 if (GetContinueOnErrors()) {
3040 a->PushBack(h, GetStateAllocator());
3041 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
3042 }
3043 RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
3044 }
3045 a->PushBack(h, GetStateAllocator());
3046 }
3047 }
3048
3049 // Remove the last token of document pointer
3050 while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
3051 ;
3052
3053 return true;
3054 }
3055
3056 void AppendToken(const Ch* str, SizeType len) {
3057 documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
3058 *documentStack_.template PushUnsafe<Ch>() = '/';
3059 for (SizeType i = 0; i < len; i++) {
3060 if (str[i] == '~') {
3061 *documentStack_.template PushUnsafe<Ch>() = '~';
3062 *documentStack_.template PushUnsafe<Ch>() = '0';
3063 }
3064 else if (str[i] == '/') {
3065 *documentStack_.template PushUnsafe<Ch>() = '~';
3066 *documentStack_.template PushUnsafe<Ch>() = '1';
3067 }
3068 else
3069 *documentStack_.template PushUnsafe<Ch>() = str[i];
3070 }
3071 }
3072
3073 RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema, flags_); }
3074
3075 RAPIDJSON_FORCEINLINE void PopSchema() {
3076 Context* c = schemaStack_.template Pop<Context>(1);
3077 if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
3078 a->~HashCodeArray();
3079 StateAllocator::Free(a);
3080 }
3081 c->~Context();
3082 }
3083
3084 void AddErrorInstanceLocation(ValueType& result, bool parent) {
3085 GenericStringBuffer<EncodingType> sb;
3086 PointerType instancePointer = GetInvalidDocumentPointer();
3087 ((parent && instancePointer.GetTokenCount() > 0)
3088 ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
3089 : instancePointer).StringifyUriFragment(sb);
3090 ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
3091 GetStateAllocator());
3092 result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
3093 }
3094
3095 void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
3096 GenericStringBuffer<EncodingType> sb;
3097 SizeType len = CurrentSchema().GetURI().GetStringLength();
3098 if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
3099 if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
3100 else GetInvalidSchemaPointer().StringifyUriFragment(sb);
3101 ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
3102 GetStateAllocator());
3103 result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
3104 }
3105
3106 void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
3107 result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
3108 }
3109
3110 void AddError(ValueType& keyword, ValueType& error) {
3111 typename ValueType::MemberIterator member = error_.FindMember(keyword);
3112 if (member == error_.MemberEnd())
3113 error_.AddMember(keyword, error, GetStateAllocator());
3114 else {
3115 if (member->value.IsObject()) {
3116 ValueType errors(kArrayType);
3117 errors.PushBack(member->value, GetStateAllocator());
3118 member->value = errors;
3119 }
3120 member->value.PushBack(error, GetStateAllocator());
3121 }
3122 }
3123
3124 void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
3125 AddErrorCode(currentError_, code);
3126 AddErrorInstanceLocation(currentError_, parent);
3127 AddErrorSchemaLocation(currentError_);
3128 AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
3129 }
3130
3131 void MergeError(ValueType& other) {
3132 for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
3133 AddError(it->name, it->value);
3134 }
3135 }
3136
3137 void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
3138 const typename SchemaType::ValueType& (*exclusive)() = 0) {
3139 currentError_.SetObject();
3140 currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
3141 currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
3142 if (exclusive)
3143 currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
3144 AddCurrentError(code);
3145 }
3146
3147 void AddErrorArray(const ValidateErrorCode code,
3148 ISchemaValidator** subvalidators, SizeType count) {
3149 ValueType errors(kArrayType);
3150 for (SizeType i = 0; i < count; ++i)
3151 errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
3152 currentError_.SetObject();
3153 currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
3154 AddCurrentError(code);
3155 }
3156
3157 const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
3158 Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
3159 const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
3160
3161 static const size_t kDefaultSchemaStackCapacity = 1024;
3162 static const size_t kDefaultDocumentStackCapacity = 256;
3163 const SchemaDocumentType* schemaDocument_;
3164 const SchemaType& root_;
3165 StateAllocator* stateAllocator_;
3166 StateAllocator* ownStateAllocator_;
3167 internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
3168 internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
3169 OutputHandler* outputHandler_;
3170 ValueType error_;
3171 ValueType currentError_;
3172 ValueType missingDependents_;
3173 bool valid_;
3174 unsigned flags_;
3175 unsigned depth_;
3176};
3177
3178typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
3179
3180///////////////////////////////////////////////////////////////////////////////
3181// SchemaValidatingReader
3182
3183//! A helper class for parsing with validation.
3184/*!
3185 This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
3186
3187 \tparam parseFlags Combination of \ref ParseFlag.
3188 \tparam InputStream Type of input stream, implementing Stream concept.
3189 \tparam SourceEncoding Encoding of the input stream.
3190 \tparam SchemaDocumentType Type of schema document.
3191 \tparam StackAllocator Allocator type for stack.
3192*/
3193template <
3194 unsigned parseFlags,
3195 typename InputStream,
3196 typename SourceEncoding,
3197 typename SchemaDocumentType = SchemaDocument,
3198 typename StackAllocator = CrtAllocator>
3200public:
3201 typedef typename SchemaDocumentType::PointerType PointerType;
3202 typedef typename InputStream::Ch Ch;
3204
3205 //! Constructor
3206 /*!
3207 \param is Input stream.
3208 \param sd Schema document.
3209 */
3210 SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
3211
3212 template <typename Handler>
3213 bool operator()(Handler& handler) {
3216 parseResult_ = reader.template Parse<parseFlags>(is_, validator);
3217
3218 isValid_ = validator.IsValid();
3219 if (isValid_) {
3220 invalidSchemaPointer_ = PointerType();
3221 invalidSchemaKeyword_ = 0;
3222 invalidDocumentPointer_ = PointerType();
3223 error_.SetObject();
3224 }
3225 else {
3226 invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
3227 invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
3228 invalidSchemaCode_ = validator.GetInvalidSchemaCode();
3229 invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
3230 error_.CopyFrom(validator.GetError(), allocator_);
3231 }
3232
3233 return parseResult_;
3234 }
3235
3236 const ParseResult& GetParseResult() const { return parseResult_; }
3237 bool IsValid() const { return isValid_; }
3238 const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
3239 const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
3240 const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
3241 const ValueType& GetError() const { return error_; }
3242 ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
3243
3244private:
3245 InputStream& is_;
3246 const SchemaDocumentType& sd_;
3247
3248 ParseResult parseResult_;
3249 PointerType invalidSchemaPointer_;
3250 const Ch* invalidSchemaKeyword_;
3251 PointerType invalidDocumentPointer_;
3252 ValidateErrorCode invalidSchemaCode_;
3253 StackAllocator allocator_;
3254 ValueType error_;
3255 bool isValid_;
3256};
3257
3258RAPIDJSON_NAMESPACE_END
3259RAPIDJSON_DIAG_POP
3260
3261#endif // RAPIDJSON_SCHEMA_H_
Concept for allocating, resizing and freeing memory block.
Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
定义 pointer.h:74
size_t GetParseErrorOffset() const
Get the parsing error offset in code unit.
定义 pointer.h:336
SAX-style JSON parser. Use Reader for UTF8 encoding and default allocator.
定义 reader.h:539
JSON schema document.
定义 schema.h:1815
static const Specification GetSpecification(const ValueType &document)
Static method to get the specification of any schema document
定义 schema.h:1942
const SchemaType & GetRoot() const
Get the root schema.
定义 schema.h:1955
void SchemaErrorPointer(const SchemaErrorCode code, const PointerType &location, const Ch *value, SizeType length, const PointerType &pointer)
Method for error with invalid pointer
定义 schema.h:1994
void SchemaErrorValue(const SchemaErrorCode code, const PointerType &location, const Ch *value, SizeType length)
定义 schema.h:1987
void SchemaError(const SchemaErrorCode code, const PointerType &location)
Default error method
定义 schema.h:1981
GValue & GetError()
Gets the error object.
定义 schema.h:1958
~GenericSchemaDocument()
Destructor
定义 schema.h:1918
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.
定义 schema.h:1843
JSON Schema Validator.
定义 schema.h:2440
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, OutputHandler &outputHandler, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor with output handler.
定义 schema.h:2487
ValidateErrorCode GetInvalidSchemaCode() const
Gets the error code of invalid schema.
定义 schema.h:2568
PointerType GetInvalidSchemaPointer() const
Gets the JSON pointer pointed to the invalid schema.
定义 schema.h:2554
PointerType GetInvalidDocumentPointer() const
Gets the JSON pointer pointed to the invalid value.
定义 schema.h:2576
const Ch * GetInvalidSchemaKeyword() const
Gets the keyword of invalid schema.
定义 schema.h:2560
ValueType & GetError()
End of Implementation of ISchemaValidator
定义 schema.h:2549
void SetValidateFlags(unsigned flags)
Implementation of ISchemaValidator
定义 schema.h:2534
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor without output handler.
定义 schema.h:2457
~GenericSchemaValidator()
Destructor.
定义 schema.h:2512
Represents an in-memory output stream.
定义 stringbuffer.h:41
size_t GetSize() const
Get the size of string in bytes in the string buffer.
定义 stringbuffer.h:82
定义 uri.h:33
Represents a JSON value. Use Value for UTF8 encoding and default allocator.
定义 fwd.h:114
Concept for receiving events from GenericReader upon parsing. The functions return true if no error o...
SchemaValidatingReader(InputStream &is, const SchemaDocumentType &sd)
Constructor
定义 schema.h:3210
#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS
User-defined kValidateDefaultFlags definition.
定义 schema.h:174
#define RAPIDJSON_ASSERT(x)
Assertion.
定义 rapidjson.h:437
ValidateErrorCode
Error codes when validating.
定义 error.h:162
SchemaErrorCode
Error codes when validating.
定义 error.h:220
@ kValidateErrors
Top level error code when kValidateContinueOnErrorsFlag set.
定义 error.h:163
@ kValidateErrorMaxItems
Array is longer than the 'maxItems' value.
定义 error.h:176
@ kValidateErrorRequired
Object is missing one or more members required by the schema.
定义 error.h:183
@ kValidateErrorMinProperties
Object has less members than 'minProperties' value.
定义 error.h:182
@ kValidateErrorMaximum
Number is greater than the 'maximum' value.
定义 error.h:167
@ kValidateErrorAdditionalProperties
Object has additional members that are not allowed by the schema.
定义 error.h:184
@ kValidateErrorNone
No error.
定义 error.h:164
@ kValidateErrorOneOf
Property did not match any of the sub-schemas specified by 'oneOf'.
定义 error.h:191
@ kValidateErrorEnum
Property has a value that is not one of its allowed enumerated values.
定义 error.h:188
@ kValidateErrorMaxLength
String is longer than the 'maxLength' value.
定义 error.h:172
@ kValidateErrorType
Property has a type that is not allowed by the schema.
定义 error.h:189
@ kValidateErrorMaxProperties
Object has more members than 'maxProperties' value.
定义 error.h:181
@ kValidateErrorNot
Property matched the sub-schema specified by 'not'.
定义 error.h:195
@ kValidateErrorExclusiveMinimum
Number is less than or equal to the 'minimum' value.
定义 error.h:170
@ kValidateErrorExclusiveMaximum
Number is greater than or equal to the 'maximum' value.
定义 error.h:168
@ kValidateErrorMultipleOf
Number is not a multiple of the 'multipleOf' value.
定义 error.h:166
@ kValidateErrorAnyOf
Property did not match any of the sub-schemas specified by 'anyOf'.
定义 error.h:194
@ kValidateErrorReadOnly
Property is read-only but has been provided when validation is for writing
定义 error.h:197
@ kValidateErrorWriteOnly
Property is write-only but has been provided when validation is for reading
定义 error.h:198
@ kValidateErrorAdditionalItems
Array has additional items that are not allowed by the schema.
定义 error.h:179
@ kValidateErrorPatternProperties
See other errors.
定义 error.h:185
@ kValidateErrorMinLength
String is longer than the 'maxLength' value.
定义 error.h:173
@ kValidateErrorMinimum
Number is less than the 'minimum' value.
定义 error.h:169
@ kValidateErrorDependencies
Object has missing property or schema dependencies.
定义 error.h:186
@ kValidateErrorMinItems
Array is shorter than the 'minItems' value.
定义 error.h:177
@ kValidateErrorOneOfMatch
Property matched more than one of the sub-schemas specified by 'oneOf'.
定义 error.h:192
@ kValidateErrorUniqueItems
Array has duplicate items but 'uniqueItems' is true.
定义 error.h:178
@ kValidateErrorPattern
String does not match the 'pattern' regular expression.
定义 error.h:174
@ kValidateErrorAllOf
Property did not match all of the sub-schemas specified by 'allOf'.
定义 error.h:193
@ kSchemaErrorStartUnknown
Pointer to start of schema does not resolve to a location in the document
定义 error.h:223
unsigned SizeType
Size type (for string lengths, array sizes, etc.)
定义 rapidjson.h:415
GenericSchemaDocument< Value, CrtAllocator > SchemaDocument
GenericSchemaDocument using Value type.
定义 fwd.h:138
OpenApiVersion
定义 schema.h:202
@ kVersionMin
Current minimum supported version
定义 schema.h:205
@ kVersionMax
Current maximum supported version
定义 schema.h:208
ValidateFlag
Combination of validate flags
定义 schema.h:178
@ kValidateWriteFlag
Validation is for a write semantic.
定义 schema.h:182
@ kValidateContinueOnErrorFlag
Don't stop after first validation error.
定义 schema.h:180
@ kValidateNoFlags
No flags are set.
定义 schema.h:179
@ kValidateReadFlag
Validation is for a read semantic.
定义 schema.h:181
@ kValidateDefaultFlags
Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
定义 schema.h:183
SchemaDraft
定义 schema.h:188
@ kDraftMin
Current minimum supported draft
定义 schema.h:192
@ kDraftMax
Current maximum supported draft
定义 schema.h:195
@ kObjectType
object
定义 rapidjson.h:733
IGenericRemoteSchemaDocumentProvider< SchemaDocument > IRemoteSchemaDocumentProvider
IGenericRemoteSchemaDocumentProvider using SchemaDocument.
定义 fwd.h:139
#define RAPIDJSON_DELETE(x)
! customization point for global delete
定义 rapidjson.h:716
#define RAPIDJSON_UINT64_C2(high32, low32)
Construct a 64-bit literal by a pair of 32-bit integer.
定义 rapidjson.h:320
#define RAPIDJSON_NEW(TypeName)
! customization point for global new
定义 rapidjson.h:712
Reference to a constant string (not taking a copy)
定义 fwd.h:111
定义 schema.h:212