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