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