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