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