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