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