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 <cmath> // abs, floor
21 
22 #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
23 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
24 #else
25 #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
26 #endif
27 
28 #if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
29 #define RAPIDJSON_SCHEMA_USE_STDREGEX 1
30 #else
31 #define RAPIDJSON_SCHEMA_USE_STDREGEX 0
32 #endif
33 
34 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
35 #include "internal/regex.h"
36 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
37 #include <regex>
38 #endif
39 
40 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
41 #define RAPIDJSON_SCHEMA_HAS_REGEX 1
42 #else
43 #define RAPIDJSON_SCHEMA_HAS_REGEX 0
44 #endif
45 
46 #ifndef RAPIDJSON_SCHEMA_VERBOSE
47 #define RAPIDJSON_SCHEMA_VERBOSE 0
48 #endif
49 
50 #if RAPIDJSON_SCHEMA_VERBOSE
51 #include "stringbuffer.h"
52 #endif
53 
54 RAPIDJSON_DIAG_PUSH
55 
56 #if defined(__GNUC__)
57 RAPIDJSON_DIAG_OFF(effc++)
58 #endif
59 
60 #ifdef __clang__
61 RAPIDJSON_DIAG_OFF(weak-vtables)
62 RAPIDJSON_DIAG_OFF(exit-time-destructors)
63 RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
64 RAPIDJSON_DIAG_OFF(variadic-macros)
65 #endif
66 
67 #ifdef _MSC_VER
68 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
69 #endif
70 
71 RAPIDJSON_NAMESPACE_BEGIN
72 
73 ///////////////////////////////////////////////////////////////////////////////
74 // Verbose Utilities
75 
76 #if RAPIDJSON_SCHEMA_VERBOSE
77 
78 namespace internal {
79 
80 inline void PrintInvalidKeyword(const char* keyword) {
81  printf("Fail keyword: %s\n", keyword);
82 }
83 
84 inline void PrintInvalidKeyword(const wchar_t* keyword) {
85  wprintf(L"Fail keyword: %ls\n", keyword);
86 }
87 
88 inline void PrintInvalidDocument(const char* document) {
89  printf("Fail document: %s\n\n", document);
90 }
91 
92 inline void PrintInvalidDocument(const wchar_t* document) {
93  wprintf(L"Fail document: %ls\n\n", document);
94 }
95 
96 inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
97  printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
98 }
99 
100 inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
101  wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
102 }
103 
104 } // namespace internal
105 
106 #endif // RAPIDJSON_SCHEMA_VERBOSE
107 
108 ///////////////////////////////////////////////////////////////////////////////
109 // RAPIDJSON_INVALID_KEYWORD_RETURN
110 
111 #if RAPIDJSON_SCHEMA_VERBOSE
112 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
113 #else
114 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
115 #endif
116 
117 #define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
118 RAPIDJSON_MULTILINEMACRO_BEGIN\
119  context.invalidKeyword = keyword.GetString();\
120  RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
121  return false;\
122 RAPIDJSON_MULTILINEMACRO_END
123 
124 ///////////////////////////////////////////////////////////////////////////////
125 // Forward declarations
126 
127 template <typename ValueType, typename Allocator>
128 class GenericSchemaDocument;
129 
130 namespace internal {
131 
132 template <typename SchemaDocumentType>
133 class Schema;
134 
135 ///////////////////////////////////////////////////////////////////////////////
136 // ISchemaValidator
137 
138 class ISchemaValidator {
139 public:
140  virtual ~ISchemaValidator() {}
141  virtual bool IsValid() const = 0;
142 };
143 
144 ///////////////////////////////////////////////////////////////////////////////
145 // ISchemaStateFactory
146 
147 template <typename SchemaType>
148 class ISchemaStateFactory {
149 public:
150  virtual ~ISchemaStateFactory() {}
151  virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
152  virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
153  virtual void* CreateHasher() = 0;
154  virtual uint64_t GetHashCode(void* hasher) = 0;
155  virtual void DestroryHasher(void* hasher) = 0;
156  virtual void* MallocState(size_t size) = 0;
157  virtual void FreeState(void* p) = 0;
158 };
159 
160 ///////////////////////////////////////////////////////////////////////////////
161 // Hasher
162 
163 // For comparison of compound value
164 template<typename Encoding, typename Allocator>
165 class Hasher {
166 public:
167  typedef typename Encoding::Ch Ch;
168 
169  Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
170 
171  bool Null() { return WriteType(kNullType); }
172  bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
173  bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
174  bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
175  bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
176  bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
177  bool Double(double d) {
178  Number n;
179  if (d < 0) n.u.i = static_cast<int64_t>(d);
180  else n.u.u = static_cast<uint64_t>(d);
181  n.d = d;
182  return WriteNumber(n);
183  }
184 
185  bool RawNumber(const Ch* str, SizeType len, bool) {
186  WriteBuffer(kNumberType, str, len * sizeof(Ch));
187  return true;
188  }
189 
190  bool String(const Ch* str, SizeType len, bool) {
191  WriteBuffer(kStringType, str, len * sizeof(Ch));
192  return true;
193  }
194 
195  bool StartObject() { return true; }
196  bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
197  bool EndObject(SizeType memberCount) {
198  uint64_t h = Hash(0, kObjectType);
199  uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
200  for (SizeType i = 0; i < memberCount; i++)
201  h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
202  *stack_.template Push<uint64_t>() = h;
203  return true;
204  }
205 
206  bool StartArray() { return true; }
207  bool EndArray(SizeType elementCount) {
208  uint64_t h = Hash(0, kArrayType);
209  uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
210  for (SizeType i = 0; i < elementCount; i++)
211  h = Hash(h, e[i]); // Use hash to achieve element order sensitive
212  *stack_.template Push<uint64_t>() = h;
213  return true;
214  }
215 
216  bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
217 
218  uint64_t GetHashCode() const {
219  RAPIDJSON_ASSERT(IsValid());
220  return *stack_.template Top<uint64_t>();
221  }
222 
223 private:
224  static const size_t kDefaultSize = 256;
225  struct Number {
226  union U {
227  uint64_t u;
228  int64_t i;
229  }u;
230  double d;
231  };
232 
233  bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
234 
235  bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
236 
237  bool WriteBuffer(Type type, const void* data, size_t len) {
238  // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
239  uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
240  const unsigned char* d = static_cast<const unsigned char*>(data);
241  for (size_t i = 0; i < len; i++)
242  h = Hash(h, d[i]);
243  *stack_.template Push<uint64_t>() = h;
244  return true;
245  }
246 
247  static uint64_t Hash(uint64_t h, uint64_t d) {
248  static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
249  h ^= d;
250  h *= kPrime;
251  return h;
252  }
253 
254  Stack<Allocator> stack_;
255 };
256 
257 ///////////////////////////////////////////////////////////////////////////////
258 // SchemaValidationContext
259 
260 template <typename SchemaDocumentType>
261 struct SchemaValidationContext {
262  typedef Schema<SchemaDocumentType> SchemaType;
263  typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
264  typedef typename SchemaType::ValueType ValueType;
265  typedef typename ValueType::Ch Ch;
266 
267  enum PatternValidatorType {
268  kPatternValidatorOnly,
269  kPatternValidatorWithProperty,
270  kPatternValidatorWithAdditionalProperty
271  };
272 
273  SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
274  factory(f),
275  schema(s),
276  valueSchema(),
277  invalidKeyword(),
278  hasher(),
279  arrayElementHashCodes(),
280  validators(),
281  validatorCount(),
282  patternPropertiesValidators(),
283  patternPropertiesValidatorCount(),
284  patternPropertiesSchemas(),
285  patternPropertiesSchemaCount(),
286  valuePatternValidatorType(kPatternValidatorOnly),
287  propertyExist(),
288  inArray(false),
289  valueUniqueness(false),
290  arrayUniqueness(false)
291  {
292  }
293 
294  ~SchemaValidationContext() {
295  if (hasher)
296  factory.DestroryHasher(hasher);
297  if (validators) {
298  for (SizeType i = 0; i < validatorCount; i++)
299  factory.DestroySchemaValidator(validators[i]);
300  factory.FreeState(validators);
301  }
302  if (patternPropertiesValidators) {
303  for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
304  factory.DestroySchemaValidator(patternPropertiesValidators[i]);
305  factory.FreeState(patternPropertiesValidators);
306  }
307  if (patternPropertiesSchemas)
308  factory.FreeState(patternPropertiesSchemas);
309  if (propertyExist)
310  factory.FreeState(propertyExist);
311  }
312 
313  SchemaValidatorFactoryType& factory;
314  const SchemaType* schema;
315  const SchemaType* valueSchema;
316  const Ch* invalidKeyword;
317  void* hasher; // Only validator access
318  void* arrayElementHashCodes; // Only validator access this
319  ISchemaValidator** validators;
320  SizeType validatorCount;
321  ISchemaValidator** patternPropertiesValidators;
322  SizeType patternPropertiesValidatorCount;
323  const SchemaType** patternPropertiesSchemas;
324  SizeType patternPropertiesSchemaCount;
325  PatternValidatorType valuePatternValidatorType;
326  PatternValidatorType objectPatternValidatorType;
327  SizeType arrayElementIndex;
328  bool* propertyExist;
329  bool inArray;
330  bool valueUniqueness;
331  bool arrayUniqueness;
332 };
333 
334 ///////////////////////////////////////////////////////////////////////////////
335 // Schema
336 
337 template <typename SchemaDocumentType>
338 class Schema {
339 public:
340  typedef typename SchemaDocumentType::ValueType ValueType;
341  typedef typename SchemaDocumentType::AllocatorType AllocatorType;
342  typedef typename SchemaDocumentType::PointerType PointerType;
343  typedef typename ValueType::EncodingType EncodingType;
344  typedef typename EncodingType::Ch Ch;
345  typedef SchemaValidationContext<SchemaDocumentType> Context;
346  typedef Schema<SchemaDocumentType> SchemaType;
347  typedef GenericValue<EncodingType, AllocatorType> SValue;
348  friend class GenericSchemaDocument<ValueType, AllocatorType>;
349 
350  Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
351  allocator_(allocator),
352  enum_(),
353  enumCount_(),
354  not_(),
355  type_((1 << kTotalSchemaType) - 1), // typeless
356  validatorCount_(),
357  properties_(),
358  additionalPropertiesSchema_(),
359  patternProperties_(),
360  patternPropertyCount_(),
361  propertyCount_(),
362  minProperties_(),
363  maxProperties_(SizeType(~0)),
364  additionalProperties_(true),
365  hasDependencies_(),
366  hasRequired_(),
367  hasSchemaDependencies_(),
368  additionalItemsSchema_(),
369  itemsList_(),
370  itemsTuple_(),
371  itemsTupleCount_(),
372  minItems_(),
373  maxItems_(SizeType(~0)),
374  additionalItems_(true),
375  uniqueItems_(false),
376  pattern_(),
377  minLength_(0),
378  maxLength_(~SizeType(0)),
379  exclusiveMinimum_(false),
380  exclusiveMaximum_(false)
381  {
382  typedef typename ValueType::ConstValueIterator ConstValueIterator;
383  typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
384 
385  if (!value.IsObject())
386  return;
387 
388  if (const ValueType* v = GetMember(value, GetTypeString())) {
389  type_ = 0;
390  if (v->IsString())
391  AddType(*v);
392  else if (v->IsArray())
393  for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
394  AddType(*itr);
395  }
396 
397  if (const ValueType* v = GetMember(value, GetEnumString()))
398  if (v->IsArray() && v->Size() > 0) {
399  enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
400  for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
401  typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
402  char buffer[256 + 24];
403  MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
404  EnumHasherType h(&hasherAllocator, 256);
405  itr->Accept(h);
406  enum_[enumCount_++] = h.GetHashCode();
407  }
408  }
409 
410  if (schemaDocument) {
411  AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
412  AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
413  AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
414  }
415 
416  if (const ValueType* v = GetMember(value, GetNotString())) {
417  schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
418  notValidatorIndex_ = validatorCount_;
419  validatorCount_++;
420  }
421 
422  // Object
423 
424  const ValueType* properties = GetMember(value, GetPropertiesString());
425  const ValueType* required = GetMember(value, GetRequiredString());
426  const ValueType* dependencies = GetMember(value, GetDependenciesString());
427  {
428  // Gather properties from properties/required/dependencies
429  SValue allProperties(kArrayType);
430 
431  if (properties && properties->IsObject())
432  for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
433  AddUniqueElement(allProperties, itr->name);
434 
435  if (required && required->IsArray())
436  for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
437  if (itr->IsString())
438  AddUniqueElement(allProperties, *itr);
439 
440  if (dependencies && dependencies->IsObject())
441  for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
442  AddUniqueElement(allProperties, itr->name);
443  if (itr->value.IsArray())
444  for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
445  if (i->IsString())
446  AddUniqueElement(allProperties, *i);
447  }
448 
449  if (allProperties.Size() > 0) {
450  propertyCount_ = allProperties.Size();
451  properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
452  for (SizeType i = 0; i < propertyCount_; i++) {
453  new (&properties_[i]) Property();
454  properties_[i].name = allProperties[i];
455  properties_[i].schema = GetTypeless();
456  }
457  }
458  }
459 
460  if (properties && properties->IsObject()) {
461  PointerType q = p.Append(GetPropertiesString(), allocator_);
462  for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
463  SizeType index;
464  if (FindPropertyIndex(itr->name, &index))
465  schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
466  }
467  }
468 
469  if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
470  PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
471  patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
472  patternPropertyCount_ = 0;
473 
474  for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
475  new (&patternProperties_[patternPropertyCount_]) PatternProperty();
476  patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
477  schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
478  patternPropertyCount_++;
479  }
480  }
481 
482  if (required && required->IsArray())
483  for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
484  if (itr->IsString()) {
485  SizeType index;
486  if (FindPropertyIndex(*itr, &index)) {
487  properties_[index].required = true;
488  hasRequired_ = true;
489  }
490  }
491 
492  if (dependencies && dependencies->IsObject()) {
493  PointerType q = p.Append(GetDependenciesString(), allocator_);
494  hasDependencies_ = true;
495  for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
496  SizeType sourceIndex;
497  if (FindPropertyIndex(itr->name, &sourceIndex)) {
498  if (itr->value.IsArray()) {
499  properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
500  std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
501  for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
502  SizeType targetIndex;
503  if (FindPropertyIndex(*targetItr, &targetIndex))
504  properties_[sourceIndex].dependencies[targetIndex] = true;
505  }
506  }
507  else if (itr->value.IsObject()) {
508  hasSchemaDependencies_ = true;
509  schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
510  properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
511  validatorCount_++;
512  }
513  }
514  }
515  }
516 
517  if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
518  if (v->IsBool())
519  additionalProperties_ = v->GetBool();
520  else if (v->IsObject())
521  schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
522  }
523 
524  AssignIfExist(minProperties_, value, GetMinPropertiesString());
525  AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
526 
527  // Array
528  if (const ValueType* v = GetMember(value, GetItemsString())) {
529  PointerType q = p.Append(GetItemsString(), allocator_);
530  if (v->IsObject()) // List validation
531  schemaDocument->CreateSchema(&itemsList_, q, *v, document);
532  else if (v->IsArray()) { // Tuple validation
533  itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
534  SizeType index = 0;
535  for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
536  schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
537  }
538  }
539 
540  AssignIfExist(minItems_, value, GetMinItemsString());
541  AssignIfExist(maxItems_, value, GetMaxItemsString());
542 
543  if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
544  if (v->IsBool())
545  additionalItems_ = v->GetBool();
546  else if (v->IsObject())
547  schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
548  }
549 
550  AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
551 
552  // String
553  AssignIfExist(minLength_, value, GetMinLengthString());
554  AssignIfExist(maxLength_, value, GetMaxLengthString());
555 
556  if (const ValueType* v = GetMember(value, GetPatternString()))
557  pattern_ = CreatePattern(*v);
558 
559  // Number
560  if (const ValueType* v = GetMember(value, GetMinimumString()))
561  if (v->IsNumber())
562  minimum_.CopyFrom(*v, *allocator_);
563 
564  if (const ValueType* v = GetMember(value, GetMaximumString()))
565  if (v->IsNumber())
566  maximum_.CopyFrom(*v, *allocator_);
567 
568  AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
569  AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
570 
571  if (const ValueType* v = GetMember(value, GetMultipleOfString()))
572  if (v->IsNumber() && v->GetDouble() > 0.0)
573  multipleOf_.CopyFrom(*v, *allocator_);
574  }
575 
576  ~Schema() {
577  if (allocator_) {
578  allocator_->Free(enum_);
579  }
580  if (properties_) {
581  for (SizeType i = 0; i < propertyCount_; i++)
582  properties_[i].~Property();
583  AllocatorType::Free(properties_);
584  }
585  if (patternProperties_) {
586  for (SizeType i = 0; i < patternPropertyCount_; i++)
587  patternProperties_[i].~PatternProperty();
588  AllocatorType::Free(patternProperties_);
589  }
590  AllocatorType::Free(itemsTuple_);
591 #if RAPIDJSON_SCHEMA_HAS_REGEX
592  if (pattern_) {
593  pattern_->~RegexType();
594  allocator_->Free(pattern_);
595  }
596 #endif
597  }
598 
599  bool BeginValue(Context& context) const {
600  if (context.inArray) {
601  if (uniqueItems_)
602  context.valueUniqueness = true;
603 
604  if (itemsList_)
605  context.valueSchema = itemsList_;
606  else if (itemsTuple_) {
607  if (context.arrayElementIndex < itemsTupleCount_)
608  context.valueSchema = itemsTuple_[context.arrayElementIndex];
609  else if (additionalItemsSchema_)
610  context.valueSchema = additionalItemsSchema_;
611  else if (additionalItems_)
612  context.valueSchema = GetTypeless();
613  else
614  RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
615  }
616  else
617  context.valueSchema = GetTypeless();
618 
619  context.arrayElementIndex++;
620  }
621  return true;
622  }
623 
624  RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
625  if (context.patternPropertiesValidatorCount > 0) {
626  bool otherValid = false;
627  SizeType count = context.patternPropertiesValidatorCount;
628  if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
629  otherValid = context.patternPropertiesValidators[--count]->IsValid();
630 
631  bool patternValid = true;
632  for (SizeType i = 0; i < count; i++)
633  if (!context.patternPropertiesValidators[i]->IsValid()) {
634  patternValid = false;
635  break;
636  }
637 
638  if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
639  if (!patternValid)
640  RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
641  }
642  else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
643  if (!patternValid || !otherValid)
644  RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
645  }
646  else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
647  RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
648  }
649 
650  if (enum_) {
651  const uint64_t h = context.factory.GetHashCode(context.hasher);
652  for (SizeType i = 0; i < enumCount_; i++)
653  if (enum_[i] == h)
654  goto foundEnum;
655  RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
656  foundEnum:;
657  }
658 
659  if (allOf_.schemas)
660  for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
661  if (!context.validators[i]->IsValid())
662  RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
663 
664  if (anyOf_.schemas) {
665  for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
666  if (context.validators[i]->IsValid())
667  goto foundAny;
668  RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
669  foundAny:;
670  }
671 
672  if (oneOf_.schemas) {
673  bool oneValid = false;
674  for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
675  if (context.validators[i]->IsValid()) {
676  if (oneValid)
677  RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
678  else
679  oneValid = true;
680  }
681  if (!oneValid)
682  RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
683  }
684 
685  if (not_ && context.validators[notValidatorIndex_]->IsValid())
686  RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
687 
688  return true;
689  }
690 
691  bool Null(Context& context) const {
692  if (!(type_ & (1 << kNullSchemaType)))
693  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
694  return CreateParallelValidator(context);
695  }
696 
697  bool Bool(Context& context, bool) const {
698  if (!(type_ & (1 << kBooleanSchemaType)))
699  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
700  return CreateParallelValidator(context);
701  }
702 
703  bool Int(Context& context, int i) const {
704  if (!CheckInt(context, i))
705  return false;
706  return CreateParallelValidator(context);
707  }
708 
709  bool Uint(Context& context, unsigned u) const {
710  if (!CheckUint(context, u))
711  return false;
712  return CreateParallelValidator(context);
713  }
714 
715  bool Int64(Context& context, int64_t i) const {
716  if (!CheckInt(context, i))
717  return false;
718  return CreateParallelValidator(context);
719  }
720 
721  bool Uint64(Context& context, uint64_t u) const {
722  if (!CheckUint(context, u))
723  return false;
724  return CreateParallelValidator(context);
725  }
726 
727  bool Double(Context& context, double d) const {
728  if (!(type_ & (1 << kNumberSchemaType)))
729  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
730 
731  if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
732  return false;
733 
734  if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
735  return false;
736 
737  if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
738  return false;
739 
740  return CreateParallelValidator(context);
741  }
742 
743  bool String(Context& context, const Ch* str, SizeType length, bool) const {
744  if (!(type_ & (1 << kStringSchemaType)))
745  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
746 
747  if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
748  SizeType count;
749  if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
750  if (count < minLength_)
751  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
752  if (count > maxLength_)
753  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
754  }
755  }
756 
757  if (pattern_ && !IsPatternMatch(pattern_, str, length))
758  RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
759 
760  return CreateParallelValidator(context);
761  }
762 
763  bool StartObject(Context& context) const {
764  if (!(type_ & (1 << kObjectSchemaType)))
765  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
766 
767  if (hasDependencies_ || hasRequired_) {
768  context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
769  std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
770  }
771 
772  if (patternProperties_) { // pre-allocate schema array
773  SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
774  context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
775  context.patternPropertiesSchemaCount = 0;
776  std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
777  }
778 
779  return CreateParallelValidator(context);
780  }
781 
782  bool Key(Context& context, const Ch* str, SizeType len, bool) const {
783  if (patternProperties_) {
784  context.patternPropertiesSchemaCount = 0;
785  for (SizeType i = 0; i < patternPropertyCount_; i++)
786  if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
787  context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
788  }
789 
790  SizeType index;
791  if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
792  if (context.patternPropertiesSchemaCount > 0) {
793  context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
794  context.valueSchema = GetTypeless();
795  context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
796  }
797  else
798  context.valueSchema = properties_[index].schema;
799 
800  if (context.propertyExist)
801  context.propertyExist[index] = true;
802 
803  return true;
804  }
805 
806  if (additionalPropertiesSchema_) {
807  if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
808  context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
809  context.valueSchema = GetTypeless();
810  context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
811  }
812  else
813  context.valueSchema = additionalPropertiesSchema_;
814  return true;
815  }
816  else if (additionalProperties_) {
817  context.valueSchema = GetTypeless();
818  return true;
819  }
820 
821  if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
822  RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
823 
824  return true;
825  }
826 
827  bool EndObject(Context& context, SizeType memberCount) const {
828  if (hasRequired_)
829  for (SizeType index = 0; index < propertyCount_; index++)
830  if (properties_[index].required)
831  if (!context.propertyExist[index])
832  RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
833 
834  if (memberCount < minProperties_)
835  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
836 
837  if (memberCount > maxProperties_)
838  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
839 
840  if (hasDependencies_) {
841  for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
842  if (context.propertyExist[sourceIndex]) {
843  if (properties_[sourceIndex].dependencies) {
844  for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
845  if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
846  RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
847  }
848  else if (properties_[sourceIndex].dependenciesSchema)
849  if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
850  RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
851  }
852  }
853 
854  return true;
855  }
856 
857  bool StartArray(Context& context) const {
858  if (!(type_ & (1 << kArraySchemaType)))
859  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
860 
861  context.arrayElementIndex = 0;
862  context.inArray = true;
863 
864  return CreateParallelValidator(context);
865  }
866 
867  bool EndArray(Context& context, SizeType elementCount) const {
868  context.inArray = false;
869 
870  if (elementCount < minItems_)
871  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
872 
873  if (elementCount > maxItems_)
874  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
875 
876  return true;
877  }
878 
879  // Generate functions for string literal according to Ch
880 #define RAPIDJSON_STRING_(name, ...) \
881  static const ValueType& Get##name##String() {\
882  static const Ch s[] = { __VA_ARGS__, '\0' };\
883  static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
884  return v;\
885  }
886 
887  RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
888  RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
889  RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
890  RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
891  RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
892  RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
893  RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
894  RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
895  RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
896  RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
897  RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
898  RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
899  RAPIDJSON_STRING_(Not, 'n', 'o', 't')
900  RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
901  RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
902  RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
903  RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
904  RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
905  RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
906  RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
907  RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
908  RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
909  RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
910  RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
911  RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
912  RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
913  RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
914  RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
915  RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
916  RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
917  RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
918  RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
919  RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
920 
921 #undef RAPIDJSON_STRING_
922 
923 private:
924  enum SchemaValueType {
925  kNullSchemaType,
926  kBooleanSchemaType,
927  kObjectSchemaType,
928  kArraySchemaType,
929  kStringSchemaType,
930  kNumberSchemaType,
931  kIntegerSchemaType,
932  kTotalSchemaType
933  };
934 
935 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
936  typedef internal::GenericRegex<EncodingType> RegexType;
937 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
938  typedef std::basic_regex<Ch> RegexType;
939 #else
940  typedef char RegexType;
941 #endif
942 
943  struct SchemaArray {
944  SchemaArray() : schemas(), count() {}
945  ~SchemaArray() { AllocatorType::Free(schemas); }
946  const SchemaType** schemas;
947  SizeType begin; // begin index of context.validators
948  SizeType count;
949  };
950 
951  static const SchemaType* GetTypeless() {
952  static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
953  return &typeless;
954  }
955 
956  template <typename V1, typename V2>
957  void AddUniqueElement(V1& a, const V2& v) {
958  for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
959  if (*itr == v)
960  return;
961  V1 c(v, *allocator_);
962  a.PushBack(c, *allocator_);
963  }
964 
965  static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
966  typename ValueType::ConstMemberIterator itr = value.FindMember(name);
967  return itr != value.MemberEnd() ? &(itr->value) : 0;
968  }
969 
970  static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
971  if (const ValueType* v = GetMember(value, name))
972  if (v->IsBool())
973  out = v->GetBool();
974  }
975 
976  static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
977  if (const ValueType* v = GetMember(value, name))
978  if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
979  out = static_cast<SizeType>(v->GetUint64());
980  }
981 
982  void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
983  if (const ValueType* v = GetMember(value, name)) {
984  if (v->IsArray() && v->Size() > 0) {
985  PointerType q = p.Append(name, allocator_);
986  out.count = v->Size();
987  out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
988  memset(out.schemas, 0, sizeof(Schema*)* out.count);
989  for (SizeType i = 0; i < out.count; i++)
990  schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
991  out.begin = validatorCount_;
992  validatorCount_ += out.count;
993  }
994  }
995  }
996 
997 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
998  template <typename ValueType>
999  RegexType* CreatePattern(const ValueType& value) {
1000  if (value.IsString()) {
1001  RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
1002  if (!r->IsValid()) {
1003  r->~RegexType();
1004  AllocatorType::Free(r);
1005  r = 0;
1006  }
1007  return r;
1008  }
1009  return 0;
1010  }
1011 
1012  static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1013  return pattern->Search(str);
1014  }
1015 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1016  template <typename ValueType>
1017  RegexType* CreatePattern(const ValueType& value) {
1018  if (value.IsString())
1019  try {
1020  return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1021  }
1022  catch (const std::regex_error&) {
1023  }
1024  return 0;
1025  }
1026 
1027  static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1028  std::match_results<const Ch*> r;
1029  return std::regex_search(str, str + length, r, *pattern);
1030  }
1031 #else
1032  template <typename ValueType>
1033  RegexType* CreatePattern(const ValueType&) { return 0; }
1034 
1035  static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1036 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1037 
1038  void AddType(const ValueType& type) {
1039  if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
1040  else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1041  else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1042  else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
1043  else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1044  else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1045  else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1046  }
1047 
1048  bool CreateParallelValidator(Context& context) const {
1049  if (enum_ || context.arrayUniqueness)
1050  context.hasher = context.factory.CreateHasher();
1051 
1052  if (validatorCount_) {
1053  RAPIDJSON_ASSERT(context.validators == 0);
1054  context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1055  context.validatorCount = validatorCount_;
1056 
1057  if (allOf_.schemas)
1058  CreateSchemaValidators(context, allOf_);
1059 
1060  if (anyOf_.schemas)
1061  CreateSchemaValidators(context, anyOf_);
1062 
1063  if (oneOf_.schemas)
1064  CreateSchemaValidators(context, oneOf_);
1065 
1066  if (not_)
1067  context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1068 
1069  if (hasSchemaDependencies_) {
1070  for (SizeType i = 0; i < propertyCount_; i++)
1071  if (properties_[i].dependenciesSchema)
1072  context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1073  }
1074  }
1075 
1076  return true;
1077  }
1078 
1079  void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1080  for (SizeType i = 0; i < schemas.count; i++)
1081  context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1082  }
1083 
1084  // O(n)
1085  bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1086  SizeType len = name.GetStringLength();
1087  const Ch* str = name.GetString();
1088  for (SizeType index = 0; index < propertyCount_; index++)
1089  if (properties_[index].name.GetStringLength() == len &&
1090  (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1091  {
1092  *outIndex = index;
1093  return true;
1094  }
1095  return false;
1096  }
1097 
1098  bool CheckInt(Context& context, int64_t i) const {
1099  if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1100  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1101 
1102  if (!minimum_.IsNull()) {
1103  if (minimum_.IsInt64()) {
1104  if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
1105  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1106  }
1107  else if (minimum_.IsUint64()) {
1108  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1109  }
1110  else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1111  return false;
1112  }
1113 
1114  if (!maximum_.IsNull()) {
1115  if (maximum_.IsInt64()) {
1116  if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
1117  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1118  }
1119  else if (maximum_.IsUint64())
1120  /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64()
1121  else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1122  return false;
1123  }
1124 
1125  if (!multipleOf_.IsNull()) {
1126  if (multipleOf_.IsUint64()) {
1127  if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
1128  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1129  }
1130  else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1131  return false;
1132  }
1133 
1134  return true;
1135  }
1136 
1137  bool CheckUint(Context& context, uint64_t i) const {
1138  if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1139  RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1140 
1141  if (!minimum_.IsNull()) {
1142  if (minimum_.IsUint64()) {
1143  if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
1144  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1145  }
1146  else if (minimum_.IsInt64())
1147  /* do nothing */; // i >= 0 > minimum.Getint64()
1148  else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1149  return false;
1150  }
1151 
1152  if (!maximum_.IsNull()) {
1153  if (maximum_.IsUint64()) {
1154  if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
1155  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1156  }
1157  else if (maximum_.IsInt64())
1158  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1159  else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1160  return false;
1161  }
1162 
1163  if (!multipleOf_.IsNull()) {
1164  if (multipleOf_.IsUint64()) {
1165  if (i % multipleOf_.GetUint64() != 0)
1166  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1167  }
1168  else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1169  return false;
1170  }
1171 
1172  return true;
1173  }
1174 
1175  bool CheckDoubleMinimum(Context& context, double d) const {
1176  if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
1177  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1178  return true;
1179  }
1180 
1181  bool CheckDoubleMaximum(Context& context, double d) const {
1182  if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
1183  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1184  return true;
1185  }
1186 
1187  bool CheckDoubleMultipleOf(Context& context, double d) const {
1188  double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1189  double q = std::floor(a / b);
1190  double r = a - q * b;
1191  if (r > 0.0)
1192  RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1193  return true;
1194  }
1195 
1196  struct Property {
1197  Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
1198  ~Property() { AllocatorType::Free(dependencies); }
1199  SValue name;
1200  const SchemaType* schema;
1201  const SchemaType* dependenciesSchema;
1202  SizeType dependenciesValidatorIndex;
1203  bool* dependencies;
1204  bool required;
1205  };
1206 
1207  struct PatternProperty {
1208  PatternProperty() : schema(), pattern() {}
1209  ~PatternProperty() {
1210  if (pattern) {
1211  pattern->~RegexType();
1212  AllocatorType::Free(pattern);
1213  }
1214  }
1215  const SchemaType* schema;
1216  RegexType* pattern;
1217  };
1218 
1219  AllocatorType* allocator_;
1220  uint64_t* enum_;
1221  SizeType enumCount_;
1222  SchemaArray allOf_;
1223  SchemaArray anyOf_;
1224  SchemaArray oneOf_;
1225  const SchemaType* not_;
1226  unsigned type_; // bitmask of kSchemaType
1227  SizeType validatorCount_;
1228  SizeType notValidatorIndex_;
1229 
1230  Property* properties_;
1231  const SchemaType* additionalPropertiesSchema_;
1232  PatternProperty* patternProperties_;
1233  SizeType patternPropertyCount_;
1234  SizeType propertyCount_;
1235  SizeType minProperties_;
1236  SizeType maxProperties_;
1237  bool additionalProperties_;
1238  bool hasDependencies_;
1239  bool hasRequired_;
1240  bool hasSchemaDependencies_;
1241 
1242  const SchemaType* additionalItemsSchema_;
1243  const SchemaType* itemsList_;
1244  const SchemaType** itemsTuple_;
1245  SizeType itemsTupleCount_;
1246  SizeType minItems_;
1247  SizeType maxItems_;
1248  bool additionalItems_;
1249  bool uniqueItems_;
1250 
1251  RegexType* pattern_;
1252  SizeType minLength_;
1253  SizeType maxLength_;
1254 
1255  SValue minimum_;
1256  SValue maximum_;
1257  SValue multipleOf_;
1258  bool exclusiveMinimum_;
1259  bool exclusiveMaximum_;
1260 };
1261 
1262 template<typename Stack, typename Ch>
1263 struct TokenHelper {
1264  RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1265  *documentStack.template Push<Ch>() = '/';
1266  char buffer[21];
1267  size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1268  for (size_t i = 0; i < length; i++)
1269  *documentStack.template Push<Ch>() = buffer[i];
1270  }
1271 };
1272 
1273 // Partial specialized version for char to prevent buffer copying.
1274 template <typename Stack>
1275 struct TokenHelper<Stack, char> {
1276  RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1277  if (sizeof(SizeType) == 4) {
1278  char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1279  *buffer++ = '/';
1280  const char* end = internal::u32toa(index, buffer);
1281  documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1282  }
1283  else {
1284  char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1285  *buffer++ = '/';
1286  const char* end = internal::u64toa(index, buffer);
1287  documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1288  }
1289  }
1290 };
1291 
1292 } // namespace internal
1293 
1294 ///////////////////////////////////////////////////////////////////////////////
1295 // IGenericRemoteSchemaDocumentProvider
1296 
1297 template <typename SchemaDocumentType>
1298 class IGenericRemoteSchemaDocumentProvider {
1299 public:
1300  typedef typename SchemaDocumentType::Ch Ch;
1301 
1302  virtual ~IGenericRemoteSchemaDocumentProvider() {}
1303  virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1304 };
1305 
1306 ///////////////////////////////////////////////////////////////////////////////
1307 // GenericSchemaDocument
1308 
1309 //! JSON schema document.
1310 /*!
1311  A JSON schema document is a compiled version of a JSON schema.
1312  It is basically a tree of internal::Schema.
1313 
1314  \note This is an immutable class (i.e. its instance cannot be modified after construction).
1315  \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1316  \tparam Allocator Allocator type for allocating memory of this document.
1317 */
1318 template <typename ValueT, typename Allocator = CrtAllocator>
1319 class GenericSchemaDocument {
1320 public:
1321  typedef ValueT ValueType;
1322  typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1323  typedef Allocator AllocatorType;
1324  typedef typename ValueType::EncodingType EncodingType;
1325  typedef typename EncodingType::Ch Ch;
1326  typedef internal::Schema<GenericSchemaDocument> SchemaType;
1327  typedef GenericPointer<ValueType, Allocator> PointerType;
1328  friend class internal::Schema<GenericSchemaDocument>;
1329  template <typename, typename, typename>
1330  friend class GenericSchemaValidator;
1331 
1332  //! Constructor.
1333  /*!
1334  Compile a JSON document into schema document.
1335 
1336  \param document A JSON document as source.
1337  \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1338  \param allocator An optional allocator instance for allocating memory. Can be null.
1339  */
1340  explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1341  remoteProvider_(remoteProvider),
1342  allocator_(allocator),
1343  ownAllocator_(),
1344  root_(),
1345  schemaMap_(allocator, kInitialSchemaMapSize),
1346  schemaRef_(allocator, kInitialSchemaRefSize)
1347  {
1348  if (!allocator_)
1349  ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
1350 
1351  // Generate root schema, it will call CreateSchema() to create sub-schemas,
1352  // And call AddRefSchema() if there are $ref.
1353  CreateSchemaRecursive(&root_, PointerType(), document, document);
1354 
1355  // Resolve $ref
1356  while (!schemaRef_.Empty()) {
1357  SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1358  if (const SchemaType* s = GetSchema(refEntry->target)) {
1359  if (refEntry->schema)
1360  *refEntry->schema = s;
1361 
1362  // Create entry in map if not exist
1363  if (!GetSchema(refEntry->source)) {
1364  new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1365  }
1366  }
1367  refEntry->~SchemaRefEntry();
1368  }
1369 
1370  RAPIDJSON_ASSERT(root_ != 0);
1371 
1372  schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1373  }
1374 
1375 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1376  //! Move constructor in C++11
1377  GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1378  remoteProvider_(rhs.remoteProvider_),
1379  allocator_(rhs.allocator_),
1380  ownAllocator_(rhs.ownAllocator_),
1381  root_(rhs.root_),
1382  schemaMap_(std::move(rhs.schemaMap_)),
1383  schemaRef_(std::move(rhs.schemaRef_))
1384  {
1385  rhs.remoteProvider_ = 0;
1386  rhs.allocator_ = 0;
1387  rhs.ownAllocator_ = 0;
1388  }
1389 #endif
1390 
1391  //! Destructor
1393  while (!schemaMap_.Empty())
1394  schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1395 
1396  RAPIDJSON_DELETE(ownAllocator_);
1397  }
1398 
1399  //! Get the root schema.
1400  const SchemaType& GetRoot() const { return *root_; }
1401 
1402 private:
1403  //! Prohibit copying
1405  //! Prohibit assignment
1406  GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1407 
1408  struct SchemaRefEntry {
1409  SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1410  PointerType source;
1411  PointerType target;
1412  const SchemaType** schema;
1413  };
1414 
1415  struct SchemaEntry {
1416  SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1417  ~SchemaEntry() {
1418  if (owned) {
1419  schema->~SchemaType();
1420  Allocator::Free(schema);
1421  }
1422  }
1423  PointerType pointer;
1424  SchemaType* schema;
1425  bool owned;
1426  };
1427 
1428  void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1429  if (schema)
1430  *schema = SchemaType::GetTypeless();
1431 
1432  if (v.GetType() == kObjectType) {
1433  const SchemaType* s = GetSchema(pointer);
1434  if (!s)
1435  CreateSchema(schema, pointer, v, document);
1436 
1437  for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1438  CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1439  }
1440  else if (v.GetType() == kArrayType)
1441  for (SizeType i = 0; i < v.Size(); i++)
1442  CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1443  }
1444 
1445  void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1446  RAPIDJSON_ASSERT(pointer.IsValid());
1447  if (v.IsObject()) {
1448  if (!HandleRefSchema(pointer, schema, v, document)) {
1449  SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1450  new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1451  if (schema)
1452  *schema = s;
1453  }
1454  }
1455  }
1456 
1457  bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1458  static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1459  static const ValueType kRefValue(kRefString, 4);
1460 
1461  typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1462  if (itr == v.MemberEnd())
1463  return false;
1464 
1465  if (itr->value.IsString()) {
1466  SizeType len = itr->value.GetStringLength();
1467  if (len > 0) {
1468  const Ch* s = itr->value.GetString();
1469  SizeType i = 0;
1470  while (i < len && s[i] != '#') // Find the first #
1471  i++;
1472 
1473  if (i > 0) { // Remote reference, resolve immediately
1474  if (remoteProvider_) {
1475  if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
1476  PointerType pointer(&s[i], len - i, allocator_);
1477  if (pointer.IsValid()) {
1478  if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1479  if (schema)
1480  *schema = sc;
1481  return true;
1482  }
1483  }
1484  }
1485  }
1486  }
1487  else if (s[i] == '#') { // Local reference, defer resolution
1488  PointerType pointer(&s[i], len - i, allocator_);
1489  if (pointer.IsValid()) {
1490  if (const ValueType* nv = pointer.Get(document))
1491  if (HandleRefSchema(source, schema, *nv, document))
1492  return true;
1493 
1494  new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1495  return true;
1496  }
1497  }
1498  }
1499  }
1500  return false;
1501  }
1502 
1503  const SchemaType* GetSchema(const PointerType& pointer) const {
1504  for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1505  if (pointer == target->pointer)
1506  return target->schema;
1507  return 0;
1508  }
1509 
1510  PointerType GetPointer(const SchemaType* schema) const {
1511  for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1512  if (schema == target->schema)
1513  return target->pointer;
1514  return PointerType();
1515  }
1516 
1517  static const size_t kInitialSchemaMapSize = 64;
1518  static const size_t kInitialSchemaRefSize = 64;
1519 
1520  IRemoteSchemaDocumentProviderType* remoteProvider_;
1521  Allocator *allocator_;
1522  Allocator *ownAllocator_;
1523  const SchemaType* root_; //!< Root schema.
1524  internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1525  internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1526 };
1527 
1528 //! GenericSchemaDocument using Value type.
1529 typedef GenericSchemaDocument<Value> SchemaDocument;
1530 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1531 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1532 
1533 ///////////////////////////////////////////////////////////////////////////////
1534 // GenericSchemaValidator
1535 
1536 //! JSON Schema Validator.
1537 /*!
1538  A SAX style JSON schema validator.
1539  It uses a \c GenericSchemaDocument to validate SAX events.
1540  It delegates the incoming SAX events to an output handler.
1541  The default output handler does nothing.
1542  It can be reused multiple times by calling \c Reset().
1543 
1544  \tparam SchemaDocumentType Type of schema document.
1545  \tparam OutputHandler Type of output handler. Default handler does nothing.
1546  \tparam StateAllocator Allocator for storing the internal validation states.
1547 */
1548 template <
1549  typename SchemaDocumentType,
1550  typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1551  typename StateAllocator = CrtAllocator>
1552 class GenericSchemaValidator :
1553  public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1554  public internal::ISchemaValidator
1555 {
1556 public:
1557  typedef typename SchemaDocumentType::SchemaType SchemaType;
1558  typedef typename SchemaDocumentType::PointerType PointerType;
1559  typedef typename SchemaType::EncodingType EncodingType;
1560  typedef typename EncodingType::Ch Ch;
1561 
1562  //! Constructor without output handler.
1563  /*!
1564  \param schemaDocument The schema document to conform to.
1565  \param allocator Optional allocator for storing internal validation states.
1566  \param schemaStackCapacity Optional initial capacity of schema path stack.
1567  \param documentStackCapacity Optional initial capacity of document path stack.
1568  */
1570  const SchemaDocumentType& schemaDocument,
1571  StateAllocator* allocator = 0,
1572  size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1573  size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1574  :
1575  schemaDocument_(&schemaDocument),
1576  root_(schemaDocument.GetRoot()),
1577  outputHandler_(GetNullHandler()),
1578  stateAllocator_(allocator),
1579  ownStateAllocator_(0),
1580  schemaStack_(allocator, schemaStackCapacity),
1581  documentStack_(allocator, documentStackCapacity),
1582  valid_(true)
1583 #if RAPIDJSON_SCHEMA_VERBOSE
1584  , depth_(0)
1585 #endif
1586  {
1587  }
1588 
1589  //! Constructor with output handler.
1590  /*!
1591  \param schemaDocument The schema document to conform to.
1592  \param allocator Optional allocator for storing internal validation states.
1593  \param schemaStackCapacity Optional initial capacity of schema path stack.
1594  \param documentStackCapacity Optional initial capacity of document path stack.
1595  */
1597  const SchemaDocumentType& schemaDocument,
1598  OutputHandler& outputHandler,
1599  StateAllocator* allocator = 0,
1600  size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1601  size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1602  :
1603  schemaDocument_(&schemaDocument),
1604  root_(schemaDocument.GetRoot()),
1605  outputHandler_(outputHandler),
1606  stateAllocator_(allocator),
1607  ownStateAllocator_(0),
1608  schemaStack_(allocator, schemaStackCapacity),
1609  documentStack_(allocator, documentStackCapacity),
1610  valid_(true)
1611 #if RAPIDJSON_SCHEMA_VERBOSE
1612  , depth_(0)
1613 #endif
1614  {
1615  }
1616 
1617  //! Destructor.
1619  Reset();
1620  RAPIDJSON_DELETE(ownStateAllocator_);
1621  }
1622 
1623  //! Reset the internal states.
1624  void Reset() {
1625  while (!schemaStack_.Empty())
1626  PopSchema();
1627  documentStack_.Clear();
1628  valid_ = true;
1629  }
1630 
1631  //! Checks whether the current state is valid.
1632  // Implementation of ISchemaValidator
1633  virtual bool IsValid() const { return valid_; }
1634 
1635  //! Gets the JSON pointer pointed to the invalid schema.
1636  PointerType GetInvalidSchemaPointer() const {
1637  return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
1638  }
1639 
1640  //! Gets the keyword of invalid schema.
1641  const Ch* GetInvalidSchemaKeyword() const {
1642  return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1643  }
1644 
1645  //! Gets the JSON pointer pointed to the invalid value.
1646  PointerType GetInvalidDocumentPointer() const {
1647  return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1648  }
1649 
1650 #if RAPIDJSON_SCHEMA_VERBOSE
1651 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
1652 RAPIDJSON_MULTILINEMACRO_BEGIN\
1653  *documentStack_.template Push<Ch>() = '\0';\
1654  documentStack_.template Pop<Ch>(1);\
1655  internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
1656 RAPIDJSON_MULTILINEMACRO_END
1657 #else
1658 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
1659 #endif
1660 
1661 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
1662  if (!valid_) return false; \
1663  if (!BeginValue() || !CurrentSchema().method arg1) {\
1664  RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
1665  return valid_ = false;\
1666  }
1667 
1668 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
1669  for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
1670  if (context->hasher)\
1671  static_cast<HasherType*>(context->hasher)->method arg2;\
1672  if (context->validators)\
1673  for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
1674  static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
1675  if (context->patternPropertiesValidators)\
1676  for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
1677  static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
1678  }
1679 
1680 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
1681  return valid_ = EndValue() && outputHandler_.method arg2
1682 
1683 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
1684  RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
1685  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
1686  RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
1687 
1688  bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
1689  bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
1690  bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
1691  bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
1692  bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
1693  bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
1694  bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
1695  bool RawNumber(const Ch* str, SizeType length, bool copy)
1696  { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1697  bool String(const Ch* str, SizeType length, bool copy)
1698  { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1699 
1700  bool StartObject() {
1701  RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
1702  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
1703  return valid_ = outputHandler_.StartObject();
1704  }
1705 
1706  bool Key(const Ch* str, SizeType len, bool copy) {
1707  if (!valid_) return false;
1708  AppendToken(str, len);
1709  if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
1710  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
1711  return valid_ = outputHandler_.Key(str, len, copy);
1712  }
1713 
1714  bool EndObject(SizeType memberCount) {
1715  if (!valid_) return false;
1716  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
1717  if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
1718  RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
1719  }
1720 
1721  bool StartArray() {
1722  RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
1723  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
1724  return valid_ = outputHandler_.StartArray();
1725  }
1726 
1727  bool EndArray(SizeType elementCount) {
1728  if (!valid_) return false;
1729  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
1730  if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
1731  RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
1732  }
1733 
1734 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
1735 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
1736 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
1737 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
1738 
1739  // Implementation of ISchemaStateFactory<SchemaType>
1740  virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
1741  return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
1742 #if RAPIDJSON_SCHEMA_VERBOSE
1743  depth_ + 1,
1744 #endif
1745  &GetStateAllocator());
1746  }
1747 
1748  virtual void DestroySchemaValidator(ISchemaValidator* validator) {
1749  GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
1750  v->~GenericSchemaValidator();
1751  StateAllocator::Free(v);
1752  }
1753 
1754  virtual void* CreateHasher() {
1755  return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
1756  }
1757 
1758  virtual uint64_t GetHashCode(void* hasher) {
1759  return static_cast<HasherType*>(hasher)->GetHashCode();
1760  }
1761 
1762  virtual void DestroryHasher(void* hasher) {
1763  HasherType* h = static_cast<HasherType*>(hasher);
1764  h->~HasherType();
1765  StateAllocator::Free(h);
1766  }
1767 
1768  virtual void* MallocState(size_t size) {
1769  return GetStateAllocator().Malloc(size);
1770  }
1771 
1772  virtual void FreeState(void* p) {
1773  return StateAllocator::Free(p);
1774  }
1775 
1776 private:
1777  typedef typename SchemaType::Context Context;
1778  typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
1779  typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
1780 
1781  GenericSchemaValidator(
1782  const SchemaDocumentType& schemaDocument,
1783  const SchemaType& root,
1784 #if RAPIDJSON_SCHEMA_VERBOSE
1785  unsigned depth,
1786 #endif
1787  StateAllocator* allocator = 0,
1788  size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1789  size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1790  :
1791  schemaDocument_(&schemaDocument),
1792  root_(root),
1793  outputHandler_(GetNullHandler()),
1794  stateAllocator_(allocator),
1795  ownStateAllocator_(0),
1796  schemaStack_(allocator, schemaStackCapacity),
1797  documentStack_(allocator, documentStackCapacity),
1798  valid_(true)
1799 #if RAPIDJSON_SCHEMA_VERBOSE
1800  , depth_(depth)
1801 #endif
1802  {
1803  }
1804 
1805  StateAllocator& GetStateAllocator() {
1806  if (!stateAllocator_)
1807  stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
1808  return *stateAllocator_;
1809  }
1810 
1811  bool BeginValue() {
1812  if (schemaStack_.Empty())
1813  PushSchema(root_);
1814  else {
1815  if (CurrentContext().inArray)
1816  internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
1817 
1818  if (!CurrentSchema().BeginValue(CurrentContext()))
1819  return false;
1820 
1821  SizeType count = CurrentContext().patternPropertiesSchemaCount;
1822  const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
1823  typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
1824  bool valueUniqueness = CurrentContext().valueUniqueness;
1825  if (CurrentContext().valueSchema)
1826  PushSchema(*CurrentContext().valueSchema);
1827 
1828  if (count > 0) {
1829  CurrentContext().objectPatternValidatorType = patternValidatorType;
1830  ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
1831  SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
1832  va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
1833  for (SizeType i = 0; i < count; i++)
1834  va[validatorCount++] = CreateSchemaValidator(*sa[i]);
1835  }
1836 
1837  CurrentContext().arrayUniqueness = valueUniqueness;
1838  }
1839  return true;
1840  }
1841 
1842  bool EndValue() {
1843  if (!CurrentSchema().EndValue(CurrentContext()))
1844  return false;
1845 
1846 #if RAPIDJSON_SCHEMA_VERBOSE
1847  GenericStringBuffer<EncodingType> sb;
1848  schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
1849 
1850  *documentStack_.template Push<Ch>() = '\0';
1851  documentStack_.template Pop<Ch>(1);
1852  internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
1853 #endif
1854 
1855  uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
1856 
1857  PopSchema();
1858 
1859  if (!schemaStack_.Empty()) {
1860  Context& context = CurrentContext();
1861  if (context.valueUniqueness) {
1862  HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
1863  if (!a)
1864  CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
1865  for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
1866  if (itr->GetUint64() == h)
1867  RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
1868  a->PushBack(h, GetStateAllocator());
1869  }
1870  }
1871 
1872  // Remove the last token of document pointer
1873  while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
1874  ;
1875 
1876  return true;
1877  }
1878 
1879  void AppendToken(const Ch* str, SizeType len) {
1880  documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
1881  *documentStack_.template PushUnsafe<Ch>() = '/';
1882  for (SizeType i = 0; i < len; i++) {
1883  if (str[i] == '~') {
1884  *documentStack_.template PushUnsafe<Ch>() = '~';
1885  *documentStack_.template PushUnsafe<Ch>() = '0';
1886  }
1887  else if (str[i] == '/') {
1888  *documentStack_.template PushUnsafe<Ch>() = '~';
1889  *documentStack_.template PushUnsafe<Ch>() = '1';
1890  }
1891  else
1892  *documentStack_.template PushUnsafe<Ch>() = str[i];
1893  }
1894  }
1895 
1896  RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
1897 
1898  RAPIDJSON_FORCEINLINE void PopSchema() {
1899  Context* c = schemaStack_.template Pop<Context>(1);
1900  if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
1901  a->~HashCodeArray();
1902  StateAllocator::Free(a);
1903  }
1904  c->~Context();
1905  }
1906 
1907  const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
1908  Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
1909  const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
1910 
1911  static OutputHandler& GetNullHandler() {
1912  static OutputHandler nullHandler;
1913  return nullHandler;
1914  }
1915 
1916  static const size_t kDefaultSchemaStackCapacity = 1024;
1917  static const size_t kDefaultDocumentStackCapacity = 256;
1918  const SchemaDocumentType* schemaDocument_;
1919  const SchemaType& root_;
1920  OutputHandler& outputHandler_;
1921  StateAllocator* stateAllocator_;
1922  StateAllocator* ownStateAllocator_;
1923  internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
1924  internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
1925  bool valid_;
1926 #if RAPIDJSON_SCHEMA_VERBOSE
1927  unsigned depth_;
1928 #endif
1929 };
1930 
1931 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
1932 
1933 ///////////////////////////////////////////////////////////////////////////////
1934 // SchemaValidatingReader
1935 
1936 //! A helper class for parsing with validation.
1937 /*!
1938  This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
1939 
1940  \tparam parseFlags Combination of \ref ParseFlag.
1941  \tparam InputStream Type of input stream, implementing Stream concept.
1942  \tparam SourceEncoding Encoding of the input stream.
1943  \tparam SchemaDocumentType Type of schema document.
1944  \tparam StackAllocator Allocator type for stack.
1945 */
1946 template <
1947  unsigned parseFlags,
1948  typename InputStream,
1949  typename SourceEncoding,
1950  typename SchemaDocumentType = SchemaDocument,
1951  typename StackAllocator = CrtAllocator>
1953 public:
1954  typedef typename SchemaDocumentType::PointerType PointerType;
1955  typedef typename InputStream::Ch Ch;
1956 
1957  //! Constructor
1958  /*!
1959  \param is Input stream.
1960  \param sd Schema document.
1961  */
1962  SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
1963 
1964  template <typename Handler>
1965  bool operator()(Handler& handler) {
1968  parseResult_ = reader.template Parse<parseFlags>(is_, validator);
1969 
1970  isValid_ = validator.IsValid();
1971  if (isValid_) {
1972  invalidSchemaPointer_ = PointerType();
1973  invalidSchemaKeyword_ = 0;
1974  invalidDocumentPointer_ = PointerType();
1975  }
1976  else {
1977  invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
1978  invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
1979  invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
1980  }
1981 
1982  return parseResult_;
1983  }
1984 
1985  const ParseResult& GetParseResult() const { return parseResult_; }
1986  bool IsValid() const { return isValid_; }
1987  const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
1988  const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
1989  const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
1990 
1991 private:
1992  InputStream& is_;
1993  const SchemaDocumentType& sd_;
1994 
1995  ParseResult parseResult_;
1996  PointerType invalidSchemaPointer_;
1997  const Ch* invalidSchemaKeyword_;
1998  PointerType invalidDocumentPointer_;
1999  bool isValid_;
2000 };
2001 
2002 RAPIDJSON_NAMESPACE_END
2003 RAPIDJSON_DIAG_POP
2004 
2005 #endif // RAPIDJSON_SCHEMA_H_
true
Definition: rapidjson.h:606
IGenericRemoteSchemaDocumentProvider< SchemaDocument > IRemoteSchemaDocumentProvider
IGenericRemoteSchemaDocumentProvider using SchemaDocument.
Definition: fwd.h:139
Concept for receiving events from GenericReader upon parsing. The functions return true if no error o...
const Ch * GetInvalidSchemaKeyword() const
Gets the keyword of invalid schema.
Definition: schema.h:1641
#define RAPIDJSON_UINT64_C2(high32, low32)
Construct a 64-bit literal by a pair of 32-bit integer.
Definition: rapidjson.h:289
SAX-style JSON parser. Use Reader for UTF8 encoding and default allocator.
Definition: fwd.h:88
GenericSchemaDocument(const ValueType &document, IRemoteSchemaDocumentProviderType *remoteProvider=0, Allocator *allocator=0)
Constructor.
Definition: schema.h:1340
unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition: rapidjson.h:380
false
Definition: rapidjson.h:605
PointerType GetInvalidDocumentPointer() const
Gets the JSON pointer pointed to the invalid value.
Definition: schema.h:1646
~GenericSchemaValidator()
Destructor.
Definition: schema.h:1618
virtual bool IsValid() const
Checks whether the current state is valid.
Definition: schema.h:1633
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor without output handler.
Definition: schema.h:1569
Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
Definition: fwd.h:126
JSON schema document.
Definition: fwd.h:136
Type
Type of JSON value.
Definition: rapidjson.h:603
object
Definition: rapidjson.h:607
void Reset()
Reset the internal states.
Definition: schema.h:1624
#define RAPIDJSON_NEW(x)
! customization point for global new
Definition: rapidjson.h:586
array
Definition: rapidjson.h:608
#define RAPIDJSON_DELETE(x)
! customization point for global delete
Definition: rapidjson.h:590
SchemaValidatingReader(InputStream &is, const SchemaDocumentType &sd)
Constructor.
Definition: schema.h:1962
~GenericSchemaDocument()
Destructor.
Definition: schema.h:1392
null
Definition: rapidjson.h:604
string
Definition: rapidjson.h:609
GenericSchemaDocument< Value, CrtAllocator > SchemaDocument
GenericSchemaDocument using Value type.
Definition: fwd.h:136
const SchemaType & GetRoot() const
Get the root schema.
Definition: schema.h:1400
JSON Schema Validator.
Definition: fwd.h:145
Concept for allocating, resizing and freeing memory block.
A helper class for parsing with validation.
Definition: schema.h:1952
PointerType GetInvalidSchemaPointer() const
Gets the JSON pointer pointed to the invalid schema.
Definition: schema.h:1636
GenericSchemaValidator(const SchemaDocumentType &schemaDocument, OutputHandler &outputHandler, StateAllocator *allocator=0, size_t schemaStackCapacity=kDefaultSchemaStackCapacity, size_t documentStackCapacity=kDefaultDocumentStackCapacity)
Constructor with output handler.
Definition: schema.h:1596
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition: rapidjson.h:402
number
Definition: rapidjson.h:610