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