rj_schema 0.2.6 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rj_schema/rapidjson/CMakeLists.txt +23 -1
  3. data/ext/rj_schema/rapidjson/appveyor.yml +49 -1
  4. data/ext/rj_schema/rapidjson/bin/types/alotofkeys.json +502 -0
  5. data/ext/rj_schema/rapidjson/bin/unittestschema/address.json +139 -0
  6. data/ext/rj_schema/rapidjson/bin/unittestschema/allOf_address.json +7 -0
  7. data/ext/rj_schema/rapidjson/bin/unittestschema/anyOf_address.json +7 -0
  8. data/ext/rj_schema/rapidjson/bin/unittestschema/idandref.json +69 -0
  9. data/ext/rj_schema/rapidjson/bin/unittestschema/oneOf_address.json +7 -0
  10. data/ext/rj_schema/rapidjson/doc/stream.md +7 -7
  11. data/ext/rj_schema/rapidjson/doc/stream.zh-cn.md +1 -1
  12. data/ext/rj_schema/rapidjson/doc/tutorial.md +15 -15
  13. data/ext/rj_schema/rapidjson/example/schemavalidator/schemavalidator.cpp +120 -0
  14. data/ext/rj_schema/rapidjson/example/traverseaspointer.cpp +39 -0
  15. data/ext/rj_schema/rapidjson/include/rapidjson/allocators.h +464 -56
  16. data/ext/rj_schema/rapidjson/include/rapidjson/cursorstreamwrapper.h +1 -1
  17. data/ext/rj_schema/rapidjson/include/rapidjson/document.h +367 -72
  18. data/ext/rj_schema/rapidjson/include/rapidjson/encodedstream.h +1 -1
  19. data/ext/rj_schema/rapidjson/include/rapidjson/encodings.h +1 -1
  20. data/ext/rj_schema/rapidjson/include/rapidjson/error/en.h +49 -1
  21. data/ext/rj_schema/rapidjson/include/rapidjson/error/error.h +56 -1
  22. data/ext/rj_schema/rapidjson/include/rapidjson/filereadstream.h +1 -1
  23. data/ext/rj_schema/rapidjson/include/rapidjson/filewritestream.h +1 -1
  24. data/ext/rj_schema/rapidjson/include/rapidjson/fwd.h +1 -1
  25. data/ext/rj_schema/rapidjson/include/rapidjson/internal/biginteger.h +1 -1
  26. data/ext/rj_schema/rapidjson/include/rapidjson/internal/clzll.h +4 -4
  27. data/ext/rj_schema/rapidjson/include/rapidjson/internal/diyfp.h +1 -1
  28. data/ext/rj_schema/rapidjson/include/rapidjson/internal/dtoa.h +1 -1
  29. data/ext/rj_schema/rapidjson/include/rapidjson/internal/ieee754.h +1 -1
  30. data/ext/rj_schema/rapidjson/include/rapidjson/internal/itoa.h +1 -1
  31. data/ext/rj_schema/rapidjson/include/rapidjson/internal/meta.h +1 -1
  32. data/ext/rj_schema/rapidjson/include/rapidjson/internal/pow10.h +1 -1
  33. data/ext/rj_schema/rapidjson/include/rapidjson/internal/regex.h +1 -1
  34. data/ext/rj_schema/rapidjson/include/rapidjson/internal/stack.h +1 -1
  35. data/ext/rj_schema/rapidjson/include/rapidjson/internal/strfunc.h +15 -1
  36. data/ext/rj_schema/rapidjson/include/rapidjson/internal/strtod.h +1 -1
  37. data/ext/rj_schema/rapidjson/include/rapidjson/internal/swap.h +1 -1
  38. data/ext/rj_schema/rapidjson/include/rapidjson/istreamwrapper.h +1 -1
  39. data/ext/rj_schema/rapidjson/include/rapidjson/memorybuffer.h +1 -1
  40. data/ext/rj_schema/rapidjson/include/rapidjson/memorystream.h +1 -1
  41. data/ext/rj_schema/rapidjson/include/rapidjson/ostreamwrapper.h +1 -1
  42. data/ext/rj_schema/rapidjson/include/rapidjson/pointer.h +69 -2
  43. data/ext/rj_schema/rapidjson/include/rapidjson/prettywriter.h +1 -1
  44. data/ext/rj_schema/rapidjson/include/rapidjson/rapidjson.h +77 -12
  45. data/ext/rj_schema/rapidjson/include/rapidjson/reader.h +17 -9
  46. data/ext/rj_schema/rapidjson/include/rapidjson/schema.h +558 -259
  47. data/ext/rj_schema/rapidjson/include/rapidjson/stream.h +1 -1
  48. data/ext/rj_schema/rapidjson/include/rapidjson/stringbuffer.h +1 -1
  49. data/ext/rj_schema/rapidjson/include/rapidjson/uri.h +466 -0
  50. data/ext/rj_schema/rapidjson/include/rapidjson/writer.h +3 -3
  51. data/ext/rj_schema/rapidjson/readme.md +3 -3
  52. data/ext/rj_schema/rapidjson/readme.zh-cn.md +2 -2
  53. data/ext/rj_schema/rapidjson/test/perftest/misctest.cpp +1 -1
  54. data/ext/rj_schema/rapidjson/test/perftest/perftest.cpp +1 -1
  55. data/ext/rj_schema/rapidjson/test/perftest/perftest.h +6 -5
  56. data/ext/rj_schema/rapidjson/test/perftest/platformtest.cpp +1 -1
  57. data/ext/rj_schema/rapidjson/test/perftest/rapidjsontest.cpp +21 -3
  58. data/ext/rj_schema/rapidjson/test/unittest/CMakeLists.txt +3 -0
  59. data/ext/rj_schema/rapidjson/test/unittest/allocatorstest.cpp +194 -2
  60. data/ext/rj_schema/rapidjson/test/unittest/bigintegertest.cpp +1 -1
  61. data/ext/rj_schema/rapidjson/test/unittest/clzlltest.cpp +34 -0
  62. data/ext/rj_schema/rapidjson/test/unittest/cursorstreamwrappertest.cpp +1 -1
  63. data/ext/rj_schema/rapidjson/test/unittest/documenttest.cpp +3 -1
  64. data/ext/rj_schema/rapidjson/test/unittest/dtoatest.cpp +1 -1
  65. data/ext/rj_schema/rapidjson/test/unittest/encodedstreamtest.cpp +1 -1
  66. data/ext/rj_schema/rapidjson/test/unittest/encodingstest.cpp +1 -1
  67. data/ext/rj_schema/rapidjson/test/unittest/filestreamtest.cpp +1 -1
  68. data/ext/rj_schema/rapidjson/test/unittest/fwdtest.cpp +1 -1
  69. data/ext/rj_schema/rapidjson/test/unittest/istreamwrappertest.cpp +1 -1
  70. data/ext/rj_schema/rapidjson/test/unittest/itoatest.cpp +1 -1
  71. data/ext/rj_schema/rapidjson/test/unittest/jsoncheckertest.cpp +1 -1
  72. data/ext/rj_schema/rapidjson/test/unittest/namespacetest.cpp +1 -1
  73. data/ext/rj_schema/rapidjson/test/unittest/ostreamwrappertest.cpp +1 -1
  74. data/ext/rj_schema/rapidjson/test/unittest/platformtest.cpp +40 -0
  75. data/ext/rj_schema/rapidjson/test/unittest/pointertest.cpp +95 -3
  76. data/ext/rj_schema/rapidjson/test/unittest/prettywritertest.cpp +1 -1
  77. data/ext/rj_schema/rapidjson/test/unittest/readertest.cpp +4 -1
  78. data/ext/rj_schema/rapidjson/test/unittest/regextest.cpp +1 -1
  79. data/ext/rj_schema/rapidjson/test/unittest/schematest.cpp +961 -81
  80. data/ext/rj_schema/rapidjson/test/unittest/simdtest.cpp +1 -1
  81. data/ext/rj_schema/rapidjson/test/unittest/strfunctest.cpp +1 -1
  82. data/ext/rj_schema/rapidjson/test/unittest/stringbuffertest.cpp +1 -1
  83. data/ext/rj_schema/rapidjson/test/unittest/strtodtest.cpp +1 -1
  84. data/ext/rj_schema/rapidjson/test/unittest/unittest.cpp +1 -1
  85. data/ext/rj_schema/rapidjson/test/unittest/unittest.h +1 -1
  86. data/ext/rj_schema/rapidjson/test/unittest/uritest.cpp +718 -0
  87. data/ext/rj_schema/rapidjson/test/unittest/valuetest.cpp +13 -3
  88. data/ext/rj_schema/rapidjson/test/unittest/writertest.cpp +1 -1
  89. data/ext/rj_schema/rj_schema.cpp +161 -17
  90. data/lib/rj_schema.rb +1 -1
  91. metadata +14 -3
@@ -18,6 +18,8 @@
18
18
  #include "document.h"
19
19
  #include "pointer.h"
20
20
  #include "stringbuffer.h"
21
+ #include "error/en.h"
22
+ #include "uri.h"
21
23
  #include <cmath> // abs, floor
22
24
 
23
25
  #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
@@ -113,13 +115,36 @@ inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar
113
115
  #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
114
116
  #endif
115
117
 
116
- #define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
118
+ #define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
117
119
  RAPIDJSON_MULTILINEMACRO_BEGIN\
118
- context.invalidKeyword = keyword.GetString();\
119
- RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
120
+ context.invalidCode = code;\
121
+ context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
122
+ RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\
120
123
  return false;\
121
124
  RAPIDJSON_MULTILINEMACRO_END
122
125
 
126
+ ///////////////////////////////////////////////////////////////////////////////
127
+ // ValidateFlag
128
+
129
+ /*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
130
+ \ingroup RAPIDJSON_CONFIG
131
+ \brief User-defined kValidateDefaultFlags definition.
132
+
133
+ User can define this as any \c ValidateFlag combinations.
134
+ */
135
+ #ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
136
+ #define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
137
+ #endif
138
+
139
+ //! Combination of validate flags
140
+ /*! \see
141
+ */
142
+ enum ValidateFlag {
143
+ kValidateNoFlags = 0, //!< No flags are set.
144
+ kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
145
+ kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
146
+ };
147
+
123
148
  ///////////////////////////////////////////////////////////////////////////////
124
149
  // Forward declarations
125
150
 
@@ -138,6 +163,8 @@ class ISchemaValidator {
138
163
  public:
139
164
  virtual ~ISchemaValidator() {}
140
165
  virtual bool IsValid() const = 0;
166
+ virtual void SetValidateFlags(unsigned flags) = 0;
167
+ virtual unsigned GetValidateFlags() const = 0;
141
168
  };
142
169
 
143
170
  ///////////////////////////////////////////////////////////////////////////////
@@ -147,7 +174,7 @@ template <typename SchemaType>
147
174
  class ISchemaStateFactory {
148
175
  public:
149
176
  virtual ~ISchemaStateFactory() {}
150
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
177
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
151
178
  virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
152
179
  virtual void* CreateHasher() = 0;
153
180
  virtual uint64_t GetHashCode(void* hasher) = 0;
@@ -201,13 +228,13 @@ public:
201
228
  virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
202
229
  virtual bool EndDependencyErrors() = 0;
203
230
 
204
- virtual void DisallowedValue() = 0;
231
+ virtual void DisallowedValue(const ValidateErrorCode code) = 0;
205
232
  virtual void StartDisallowedType() = 0;
206
233
  virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
207
234
  virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
208
235
  virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
209
236
  virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
210
- virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
237
+ virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0;
211
238
  virtual void Disallowed() = 0;
212
239
  };
213
240
 
@@ -332,6 +359,7 @@ struct SchemaValidationContext {
332
359
  schema(s),
333
360
  valueSchema(),
334
361
  invalidKeyword(),
362
+ invalidCode(),
335
363
  hasher(),
336
364
  arrayElementHashCodes(),
337
365
  validators(),
@@ -372,6 +400,7 @@ struct SchemaValidationContext {
372
400
  const SchemaType* schema;
373
401
  const SchemaType* valueSchema;
374
402
  const Ch* invalidKeyword;
403
+ ValidateErrorCode invalidCode;
375
404
  void* hasher; // Only validator access
376
405
  void* arrayElementHashCodes; // Only validator access this
377
406
  ISchemaValidator** validators;
@@ -404,11 +433,13 @@ public:
404
433
  typedef Schema<SchemaDocumentType> SchemaType;
405
434
  typedef GenericValue<EncodingType, AllocatorType> SValue;
406
435
  typedef IValidationErrorHandler<Schema> ErrorHandler;
436
+ typedef GenericUri<ValueType, AllocatorType> UriType;
407
437
  friend class GenericSchemaDocument<ValueType, AllocatorType>;
408
438
 
409
- Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
439
+ Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
410
440
  allocator_(allocator),
411
441
  uri_(schemaDocument->GetURI(), *allocator),
442
+ id_(id),
412
443
  pointer_(p, allocator),
413
444
  typeless_(schemaDocument->GetTypeless()),
414
445
  enum_(),
@@ -446,9 +477,28 @@ public:
446
477
  typedef typename ValueType::ConstValueIterator ConstValueIterator;
447
478
  typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
448
479
 
480
+ // PR #1393
481
+ // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite
482
+ // recursion (with recursive schemas), since schemaDocument->getSchema() is always
483
+ // checked before creating a new one. Don't cache typeless_, though.
484
+ if (this != typeless_) {
485
+ typedef typename SchemaDocumentType::SchemaEntry SchemaEntry;
486
+ SchemaEntry *entry = schemaDocument->schemaMap_.template Push<SchemaEntry>();
487
+ new (entry) SchemaEntry(pointer_, this, true, allocator_);
488
+ schemaDocument->AddSchemaRefs(this);
489
+ }
490
+
449
491
  if (!value.IsObject())
450
492
  return;
451
493
 
494
+ // If we have an id property, resolve it with the in-scope id
495
+ if (const ValueType* v = GetMember(value, GetIdString())) {
496
+ if (v->IsString()) {
497
+ UriType local(*v, allocator);
498
+ id_ = local.Resolve(id_, allocator);
499
+ }
500
+ }
501
+
452
502
  if (const ValueType* v = GetMember(value, GetTypeString())) {
453
503
  type_ = 0;
454
504
  if (v->IsString())
@@ -458,7 +508,7 @@ public:
458
508
  AddType(*itr);
459
509
  }
460
510
 
461
- if (const ValueType* v = GetMember(value, GetEnumString()))
511
+ if (const ValueType* v = GetMember(value, GetEnumString())) {
462
512
  if (v->IsArray() && v->Size() > 0) {
463
513
  enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
464
514
  for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
@@ -470,17 +520,18 @@ public:
470
520
  enum_[enumCount_++] = h.GetHashCode();
471
521
  }
472
522
  }
523
+ }
473
524
 
474
525
  if (schemaDocument) {
475
526
  AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
476
527
  AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
477
528
  AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
478
- }
479
529
 
480
- if (const ValueType* v = GetMember(value, GetNotString())) {
481
- schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
482
- notValidatorIndex_ = validatorCount_;
483
- validatorCount_++;
530
+ if (const ValueType* v = GetMember(value, GetNotString())) {
531
+ schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document, id_);
532
+ notValidatorIndex_ = validatorCount_;
533
+ validatorCount_++;
534
+ }
484
535
  }
485
536
 
486
537
  // Object
@@ -495,7 +546,7 @@ public:
495
546
  if (properties && properties->IsObject())
496
547
  for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
497
548
  AddUniqueElement(allProperties, itr->name);
498
-
549
+
499
550
  if (required && required->IsArray())
500
551
  for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
501
552
  if (itr->IsString())
@@ -526,7 +577,7 @@ public:
526
577
  for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
527
578
  SizeType index;
528
579
  if (FindPropertyIndex(itr->name, &index))
529
- schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
580
+ schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
530
581
  }
531
582
  }
532
583
 
@@ -538,7 +589,7 @@ public:
538
589
  for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
539
590
  new (&patternProperties_[patternPropertyCount_]) PatternProperty();
540
591
  patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
541
- schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
592
+ schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
542
593
  patternPropertyCount_++;
543
594
  }
544
595
  }
@@ -570,7 +621,7 @@ public:
570
621
  }
571
622
  else if (itr->value.IsObject()) {
572
623
  hasSchemaDependencies_ = true;
573
- schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
624
+ schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_);
574
625
  properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
575
626
  validatorCount_++;
576
627
  }
@@ -582,7 +633,7 @@ public:
582
633
  if (v->IsBool())
583
634
  additionalProperties_ = v->GetBool();
584
635
  else if (v->IsObject())
585
- schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
636
+ schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_);
586
637
  }
587
638
 
588
639
  AssignIfExist(minProperties_, value, GetMinPropertiesString());
@@ -592,12 +643,12 @@ public:
592
643
  if (const ValueType* v = GetMember(value, GetItemsString())) {
593
644
  PointerType q = p.Append(GetItemsString(), allocator_);
594
645
  if (v->IsObject()) // List validation
595
- schemaDocument->CreateSchema(&itemsList_, q, *v, document);
646
+ schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_);
596
647
  else if (v->IsArray()) { // Tuple validation
597
648
  itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
598
649
  SizeType index = 0;
599
650
  for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
600
- schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
651
+ schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_);
601
652
  }
602
653
  }
603
654
 
@@ -608,7 +659,7 @@ public:
608
659
  if (v->IsBool())
609
660
  additionalItems_ = v->GetBool();
610
661
  else if (v->IsObject())
611
- schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
662
+ schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_);
612
663
  }
613
664
 
614
665
  AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
@@ -668,6 +719,10 @@ public:
668
719
  return uri_;
669
720
  }
670
721
 
722
+ const UriType& GetId() const {
723
+ return id_;
724
+ }
725
+
671
726
  const PointerType& GetPointer() const {
672
727
  return pointer_;
673
728
  }
@@ -688,7 +743,11 @@ public:
688
743
  context.valueSchema = typeless_;
689
744
  else {
690
745
  context.error_handler.DisallowedItem(context.arrayElementIndex);
691
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
746
+ // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
747
+ context.valueSchema = typeless_;
748
+ // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
749
+ context.arrayElementIndex++;
750
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
692
751
  }
693
752
  }
694
753
  else
@@ -700,6 +759,7 @@ public:
700
759
  }
701
760
 
702
761
  RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
762
+ // Only check pattern properties if we have validators
703
763
  if (context.patternPropertiesValidatorCount > 0) {
704
764
  bool otherValid = false;
705
765
  SizeType count = context.patternPropertiesValidatorCount;
@@ -716,66 +776,70 @@ public:
716
776
  if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
717
777
  if (!patternValid) {
718
778
  context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
719
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
779
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
720
780
  }
721
781
  }
722
782
  else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
723
783
  if (!patternValid || !otherValid) {
724
784
  context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
725
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
785
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
726
786
  }
727
787
  }
728
788
  else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
729
789
  context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
730
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
790
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
731
791
  }
732
792
  }
733
793
 
734
- if (enum_) {
794
+ // For enums only check if we have a hasher
795
+ if (enum_ && context.hasher) {
735
796
  const uint64_t h = context.factory.GetHashCode(context.hasher);
736
797
  for (SizeType i = 0; i < enumCount_; i++)
737
798
  if (enum_[i] == h)
738
799
  goto foundEnum;
739
- context.error_handler.DisallowedValue();
740
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
800
+ context.error_handler.DisallowedValue(kValidateErrorEnum);
801
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
741
802
  foundEnum:;
742
803
  }
743
804
 
744
- if (allOf_.schemas)
745
- for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
746
- if (!context.validators[i]->IsValid()) {
747
- context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
748
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
749
- }
750
-
751
- if (anyOf_.schemas) {
752
- for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
753
- if (context.validators[i]->IsValid())
754
- goto foundAny;
755
- context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
756
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
757
- foundAny:;
758
- }
759
-
760
- if (oneOf_.schemas) {
761
- bool oneValid = false;
762
- for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
763
- if (context.validators[i]->IsValid()) {
764
- if (oneValid) {
765
- context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
766
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
767
- } else
768
- oneValid = true;
805
+ // Only check allOf etc if we have validators
806
+ if (context.validatorCount > 0) {
807
+ if (allOf_.schemas)
808
+ for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
809
+ if (!context.validators[i]->IsValid()) {
810
+ context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
811
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
812
+ }
813
+
814
+ if (anyOf_.schemas) {
815
+ for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
816
+ if (context.validators[i]->IsValid())
817
+ goto foundAny;
818
+ context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
819
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
820
+ foundAny:;
821
+ }
822
+
823
+ if (oneOf_.schemas) {
824
+ bool oneValid = false;
825
+ for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
826
+ if (context.validators[i]->IsValid()) {
827
+ if (oneValid) {
828
+ context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true);
829
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
830
+ } else
831
+ oneValid = true;
832
+ }
833
+ if (!oneValid) {
834
+ context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false);
835
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
769
836
  }
770
- if (!oneValid) {
771
- context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
772
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
773
837
  }
774
- }
775
838
 
776
- if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
777
- context.error_handler.Disallowed();
778
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
839
+ if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
840
+ context.error_handler.Disallowed();
841
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
842
+ }
779
843
  }
780
844
 
781
845
  return true;
@@ -784,15 +848,15 @@ public:
784
848
  bool Null(Context& context) const {
785
849
  if (!(type_ & (1 << kNullSchemaType))) {
786
850
  DisallowedType(context, GetNullString());
787
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
851
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
788
852
  }
789
853
  return CreateParallelValidator(context);
790
854
  }
791
-
855
+
792
856
  bool Bool(Context& context, bool) const {
793
857
  if (!(type_ & (1 << kBooleanSchemaType))) {
794
858
  DisallowedType(context, GetBooleanString());
795
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
859
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
796
860
  }
797
861
  return CreateParallelValidator(context);
798
862
  }
@@ -824,7 +888,7 @@ public:
824
888
  bool Double(Context& context, double d) const {
825
889
  if (!(type_ & (1 << kNumberSchemaType))) {
826
890
  DisallowedType(context, GetNumberString());
827
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
891
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
828
892
  }
829
893
 
830
894
  if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
@@ -832,17 +896,17 @@ public:
832
896
 
833
897
  if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
834
898
  return false;
835
-
899
+
836
900
  if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
837
901
  return false;
838
-
902
+
839
903
  return CreateParallelValidator(context);
840
904
  }
841
-
905
+
842
906
  bool String(Context& context, const Ch* str, SizeType length, bool) const {
843
907
  if (!(type_ & (1 << kStringSchemaType))) {
844
908
  DisallowedType(context, GetStringString());
845
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
909
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
846
910
  }
847
911
 
848
912
  if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
@@ -850,18 +914,18 @@ public:
850
914
  if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
851
915
  if (count < minLength_) {
852
916
  context.error_handler.TooShort(str, length, minLength_);
853
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
917
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
854
918
  }
855
919
  if (count > maxLength_) {
856
920
  context.error_handler.TooLong(str, length, maxLength_);
857
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
921
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
858
922
  }
859
923
  }
860
924
  }
861
925
 
862
926
  if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
863
927
  context.error_handler.DoesNotMatch(str, length);
864
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
928
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
865
929
  }
866
930
 
867
931
  return CreateParallelValidator(context);
@@ -870,7 +934,7 @@ public:
870
934
  bool StartObject(Context& context) const {
871
935
  if (!(type_ & (1 << kObjectSchemaType))) {
872
936
  DisallowedType(context, GetObjectString());
873
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
937
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
874
938
  }
875
939
 
876
940
  if (hasDependencies_ || hasRequired_) {
@@ -887,7 +951,7 @@ public:
887
951
 
888
952
  return CreateParallelValidator(context);
889
953
  }
890
-
954
+
891
955
  bool Key(Context& context, const Ch* str, SizeType len, bool) const {
892
956
  if (patternProperties_) {
893
957
  context.patternPropertiesSchemaCount = 0;
@@ -915,7 +979,7 @@ public:
915
979
  }
916
980
 
917
981
  if (additionalPropertiesSchema_) {
918
- if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
982
+ if (context.patternPropertiesSchemaCount > 0) {
919
983
  context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
920
984
  context.valueSchema = typeless_;
921
985
  context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
@@ -930,8 +994,10 @@ public:
930
994
  }
931
995
 
932
996
  if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
997
+ // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
998
+ context.valueSchema = typeless_;
933
999
  context.error_handler.DisallowedProperty(str, len);
934
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
1000
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
935
1001
  }
936
1002
 
937
1003
  return true;
@@ -945,17 +1011,17 @@ public:
945
1011
  if (properties_[index].schema->defaultValueLength_ == 0 )
946
1012
  context.error_handler.AddMissingProperty(properties_[index].name);
947
1013
  if (context.error_handler.EndMissingProperties())
948
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
1014
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
949
1015
  }
950
1016
 
951
1017
  if (memberCount < minProperties_) {
952
1018
  context.error_handler.TooFewProperties(memberCount, minProperties_);
953
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
1019
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
954
1020
  }
955
1021
 
956
1022
  if (memberCount > maxProperties_) {
957
1023
  context.error_handler.TooManyProperties(memberCount, maxProperties_);
958
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
1024
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
959
1025
  }
960
1026
 
961
1027
  if (hasDependencies_) {
@@ -978,40 +1044,78 @@ public:
978
1044
  }
979
1045
  }
980
1046
  if (context.error_handler.EndDependencyErrors())
981
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
1047
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
982
1048
  }
983
1049
 
984
1050
  return true;
985
1051
  }
986
1052
 
987
1053
  bool StartArray(Context& context) const {
1054
+ context.arrayElementIndex = 0;
1055
+ context.inArray = true; // Ensure we note that we are in an array
1056
+
988
1057
  if (!(type_ & (1 << kArraySchemaType))) {
989
1058
  DisallowedType(context, GetArrayString());
990
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1059
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
991
1060
  }
992
1061
 
993
- context.arrayElementIndex = 0;
994
- context.inArray = true;
995
-
996
1062
  return CreateParallelValidator(context);
997
1063
  }
998
1064
 
999
1065
  bool EndArray(Context& context, SizeType elementCount) const {
1000
1066
  context.inArray = false;
1001
-
1067
+
1002
1068
  if (elementCount < minItems_) {
1003
1069
  context.error_handler.TooFewItems(elementCount, minItems_);
1004
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
1070
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
1005
1071
  }
1006
-
1072
+
1007
1073
  if (elementCount > maxItems_) {
1008
1074
  context.error_handler.TooManyItems(elementCount, maxItems_);
1009
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
1075
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
1010
1076
  }
1011
1077
 
1012
1078
  return true;
1013
1079
  }
1014
1080
 
1081
+ static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
1082
+ switch (validateErrorCode) {
1083
+ case kValidateErrorMultipleOf: return GetMultipleOfString();
1084
+ case kValidateErrorMaximum: return GetMaximumString();
1085
+ case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
1086
+ case kValidateErrorMinimum: return GetMinimumString();
1087
+ case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
1088
+
1089
+ case kValidateErrorMaxLength: return GetMaxLengthString();
1090
+ case kValidateErrorMinLength: return GetMinLengthString();
1091
+ case kValidateErrorPattern: return GetPatternString();
1092
+
1093
+ case kValidateErrorMaxItems: return GetMaxItemsString();
1094
+ case kValidateErrorMinItems: return GetMinItemsString();
1095
+ case kValidateErrorUniqueItems: return GetUniqueItemsString();
1096
+ case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
1097
+
1098
+ case kValidateErrorMaxProperties: return GetMaxPropertiesString();
1099
+ case kValidateErrorMinProperties: return GetMinPropertiesString();
1100
+ case kValidateErrorRequired: return GetRequiredString();
1101
+ case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
1102
+ case kValidateErrorPatternProperties: return GetPatternPropertiesString();
1103
+ case kValidateErrorDependencies: return GetDependenciesString();
1104
+
1105
+ case kValidateErrorEnum: return GetEnumString();
1106
+ case kValidateErrorType: return GetTypeString();
1107
+
1108
+ case kValidateErrorOneOf: return GetOneOfString();
1109
+ case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
1110
+ case kValidateErrorAllOf: return GetAllOfString();
1111
+ case kValidateErrorAnyOf: return GetAnyOfString();
1112
+ case kValidateErrorNot: return GetNotString();
1113
+
1114
+ default: return GetNullString();
1115
+ }
1116
+ }
1117
+
1118
+
1015
1119
  // Generate functions for string literal according to Ch
1016
1120
  #define RAPIDJSON_STRING_(name, ...) \
1017
1121
  static const ValueType& Get##name##String() {\
@@ -1054,6 +1158,15 @@ public:
1054
1158
  RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
1055
1159
  RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
1056
1160
  RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
1161
+ RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
1162
+ RAPIDJSON_STRING_(Id, 'i', 'd')
1163
+
1164
+ RAPIDJSON_STRING_(SchemeEnd, ':')
1165
+ RAPIDJSON_STRING_(AuthStart, '/', '/')
1166
+ RAPIDJSON_STRING_(QueryStart, '?')
1167
+ RAPIDJSON_STRING_(FragStart, '#')
1168
+ RAPIDJSON_STRING_(Slash, '/')
1169
+ RAPIDJSON_STRING_(Dot, '.')
1057
1170
 
1058
1171
  #undef RAPIDJSON_STRING_
1059
1172
 
@@ -1119,7 +1232,7 @@ private:
1119
1232
  out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
1120
1233
  memset(out.schemas, 0, sizeof(Schema*)* out.count);
1121
1234
  for (SizeType i = 0; i < out.count; i++)
1122
- schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
1235
+ schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_);
1123
1236
  out.begin = validatorCount_;
1124
1237
  validatorCount_ += out.count;
1125
1238
  }
@@ -1190,31 +1303,32 @@ private:
1190
1303
  context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1191
1304
  context.validatorCount = validatorCount_;
1192
1305
 
1306
+ // Always return after first failure for these sub-validators
1193
1307
  if (allOf_.schemas)
1194
- CreateSchemaValidators(context, allOf_);
1308
+ CreateSchemaValidators(context, allOf_, false);
1195
1309
 
1196
1310
  if (anyOf_.schemas)
1197
- CreateSchemaValidators(context, anyOf_);
1198
-
1311
+ CreateSchemaValidators(context, anyOf_, false);
1312
+
1199
1313
  if (oneOf_.schemas)
1200
- CreateSchemaValidators(context, oneOf_);
1201
-
1314
+ CreateSchemaValidators(context, oneOf_, false);
1315
+
1202
1316
  if (not_)
1203
- context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1204
-
1317
+ context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
1318
+
1205
1319
  if (hasSchemaDependencies_) {
1206
1320
  for (SizeType i = 0; i < propertyCount_; i++)
1207
1321
  if (properties_[i].dependenciesSchema)
1208
- context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1322
+ context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
1209
1323
  }
1210
1324
  }
1211
1325
 
1212
1326
  return true;
1213
1327
  }
1214
1328
 
1215
- void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1329
+ void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
1216
1330
  for (SizeType i = 0; i < schemas.count; i++)
1217
- context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1331
+ context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
1218
1332
  }
1219
1333
 
1220
1334
  // O(n)
@@ -1222,7 +1336,7 @@ private:
1222
1336
  SizeType len = name.GetStringLength();
1223
1337
  const Ch* str = name.GetString();
1224
1338
  for (SizeType index = 0; index < propertyCount_; index++)
1225
- if (properties_[index].name.GetStringLength() == len &&
1339
+ if (properties_[index].name.GetStringLength() == len &&
1226
1340
  (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1227
1341
  {
1228
1342
  *outIndex = index;
@@ -1234,19 +1348,19 @@ private:
1234
1348
  bool CheckInt(Context& context, int64_t i) const {
1235
1349
  if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1236
1350
  DisallowedType(context, GetIntegerString());
1237
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1351
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1238
1352
  }
1239
1353
 
1240
1354
  if (!minimum_.IsNull()) {
1241
1355
  if (minimum_.IsInt64()) {
1242
1356
  if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
1243
1357
  context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1244
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1358
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1245
1359
  }
1246
1360
  }
1247
1361
  else if (minimum_.IsUint64()) {
1248
1362
  context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1249
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1363
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
1250
1364
  }
1251
1365
  else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1252
1366
  return false;
@@ -1256,7 +1370,7 @@ private:
1256
1370
  if (maximum_.IsInt64()) {
1257
1371
  if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
1258
1372
  context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1259
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1373
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1260
1374
  }
1261
1375
  }
1262
1376
  else if (maximum_.IsUint64()) { }
@@ -1269,7 +1383,7 @@ private:
1269
1383
  if (multipleOf_.IsUint64()) {
1270
1384
  if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
1271
1385
  context.error_handler.NotMultipleOf(i, multipleOf_);
1272
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1386
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1273
1387
  }
1274
1388
  }
1275
1389
  else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
@@ -1282,14 +1396,14 @@ private:
1282
1396
  bool CheckUint(Context& context, uint64_t i) const {
1283
1397
  if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
1284
1398
  DisallowedType(context, GetIntegerString());
1285
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1399
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
1286
1400
  }
1287
1401
 
1288
1402
  if (!minimum_.IsNull()) {
1289
1403
  if (minimum_.IsUint64()) {
1290
1404
  if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
1291
1405
  context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
1292
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1406
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1293
1407
  }
1294
1408
  }
1295
1409
  else if (minimum_.IsInt64())
@@ -1302,12 +1416,12 @@ private:
1302
1416
  if (maximum_.IsUint64()) {
1303
1417
  if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
1304
1418
  context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1305
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1419
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1306
1420
  }
1307
1421
  }
1308
1422
  else if (maximum_.IsInt64()) {
1309
1423
  context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
1310
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1424
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
1311
1425
  }
1312
1426
  else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1313
1427
  return false;
@@ -1317,7 +1431,7 @@ private:
1317
1431
  if (multipleOf_.IsUint64()) {
1318
1432
  if (i % multipleOf_.GetUint64() != 0) {
1319
1433
  context.error_handler.NotMultipleOf(i, multipleOf_);
1320
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1434
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1321
1435
  }
1322
1436
  }
1323
1437
  else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
@@ -1330,7 +1444,7 @@ private:
1330
1444
  bool CheckDoubleMinimum(Context& context, double d) const {
1331
1445
  if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
1332
1446
  context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
1333
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1447
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
1334
1448
  }
1335
1449
  return true;
1336
1450
  }
@@ -1338,7 +1452,7 @@ private:
1338
1452
  bool CheckDoubleMaximum(Context& context, double d) const {
1339
1453
  if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
1340
1454
  context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
1341
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1455
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
1342
1456
  }
1343
1457
  return true;
1344
1458
  }
@@ -1349,7 +1463,7 @@ private:
1349
1463
  double r = a - q * b;
1350
1464
  if (r > 0.0) {
1351
1465
  context.error_handler.NotMultipleOf(d, multipleOf_);
1352
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1466
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
1353
1467
  }
1354
1468
  return true;
1355
1469
  }
@@ -1383,7 +1497,7 @@ private:
1383
1497
 
1384
1498
  struct PatternProperty {
1385
1499
  PatternProperty() : schema(), pattern() {}
1386
- ~PatternProperty() {
1500
+ ~PatternProperty() {
1387
1501
  if (pattern) {
1388
1502
  pattern->~RegexType();
1389
1503
  AllocatorType::Free(pattern);
@@ -1395,6 +1509,7 @@ private:
1395
1509
 
1396
1510
  AllocatorType* allocator_;
1397
1511
  SValue uri_;
1512
+ UriType id_;
1398
1513
  PointerType pointer_;
1399
1514
  const SchemaType* typeless_;
1400
1515
  uint64_t* enum_;
@@ -1437,7 +1552,7 @@ private:
1437
1552
  SValue multipleOf_;
1438
1553
  bool exclusiveMinimum_;
1439
1554
  bool exclusiveMaximum_;
1440
-
1555
+
1441
1556
  SizeType defaultValueLength_;
1442
1557
  };
1443
1558
 
@@ -1480,9 +1595,12 @@ template <typename SchemaDocumentType>
1480
1595
  class IGenericRemoteSchemaDocumentProvider {
1481
1596
  public:
1482
1597
  typedef typename SchemaDocumentType::Ch Ch;
1598
+ typedef typename SchemaDocumentType::ValueType ValueType;
1599
+ typedef typename SchemaDocumentType::AllocatorType AllocatorType;
1483
1600
 
1484
1601
  virtual ~IGenericRemoteSchemaDocumentProvider() {}
1485
1602
  virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1603
+ virtual const SchemaDocumentType* GetRemoteDocument(GenericUri<ValueType, AllocatorType> uri) { return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); }
1486
1604
  };
1487
1605
 
1488
1606
  ///////////////////////////////////////////////////////////////////////////////
@@ -1507,7 +1625,8 @@ public:
1507
1625
  typedef typename EncodingType::Ch Ch;
1508
1626
  typedef internal::Schema<GenericSchemaDocument> SchemaType;
1509
1627
  typedef GenericPointer<ValueType, Allocator> PointerType;
1510
- typedef GenericValue<EncodingType, Allocator> URIType;
1628
+ typedef GenericValue<EncodingType, AllocatorType> SValue;
1629
+ typedef GenericUri<ValueType, Allocator> UriType;
1511
1630
  friend class internal::Schema<GenericSchemaDocument>;
1512
1631
  template <typename, typename, typename>
1513
1632
  friend class GenericSchemaValidator;
@@ -1521,9 +1640,11 @@ public:
1521
1640
  \param uriLength Length of \c name, in code points.
1522
1641
  \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1523
1642
  \param allocator An optional allocator instance for allocating memory. Can be null.
1643
+ \param pointer An optional JSON pointer to the start of the schema document
1524
1644
  */
1525
1645
  explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
1526
- IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1646
+ IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
1647
+ const PointerType& pointer = PointerType()) : // PR #1393
1527
1648
  remoteProvider_(remoteProvider),
1528
1649
  allocator_(allocator),
1529
1650
  ownAllocator_(),
@@ -1537,30 +1658,20 @@ public:
1537
1658
 
1538
1659
  Ch noUri[1] = {0};
1539
1660
  uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
1661
+ docId_ = UriType(uri_, allocator_);
1540
1662
 
1541
1663
  typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1542
- new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_);
1664
+ new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
1543
1665
 
1544
1666
  // Generate root schema, it will call CreateSchema() to create sub-schemas,
1545
- // And call AddRefSchema() if there are $ref.
1546
- CreateSchemaRecursive(&root_, PointerType(), document, document);
1547
-
1548
- // Resolve $ref
1549
- while (!schemaRef_.Empty()) {
1550
- SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1551
- if (const SchemaType* s = GetSchema(refEntry->target)) {
1552
- if (refEntry->schema)
1553
- *refEntry->schema = s;
1554
-
1555
- // Create entry in map if not exist
1556
- if (!GetSchema(refEntry->source)) {
1557
- new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1558
- }
1559
- }
1560
- else if (refEntry->schema)
1561
- *refEntry->schema = typeless_;
1562
-
1563
- refEntry->~SchemaRefEntry();
1667
+ // And call HandleRefSchema() if there are $ref.
1668
+ // PR #1393 use input pointer if supplied
1669
+ root_ = typeless_;
1670
+ if (pointer.GetTokenCount() == 0) {
1671
+ CreateSchemaRecursive(&root_, pointer, document, document, docId_);
1672
+ }
1673
+ else if (const ValueType* v = pointer.Get(document)) {
1674
+ CreateSchema(&root_, pointer, *v, document, docId_);
1564
1675
  }
1565
1676
 
1566
1677
  RAPIDJSON_ASSERT(root_ != 0);
@@ -1578,7 +1689,8 @@ public:
1578
1689
  typeless_(rhs.typeless_),
1579
1690
  schemaMap_(std::move(rhs.schemaMap_)),
1580
1691
  schemaRef_(std::move(rhs.schemaRef_)),
1581
- uri_(std::move(rhs.uri_))
1692
+ uri_(std::move(rhs.uri_)),
1693
+ docId_(rhs.docId_)
1582
1694
  {
1583
1695
  rhs.remoteProvider_ = 0;
1584
1696
  rhs.allocator_ = 0;
@@ -1600,7 +1712,7 @@ public:
1600
1712
  RAPIDJSON_DELETE(ownAllocator_);
1601
1713
  }
1602
1714
 
1603
- const URIType& GetURI() const { return uri_; }
1715
+ const SValue& GetURI() const { return uri_; }
1604
1716
 
1605
1717
  //! Get the root schema.
1606
1718
  const SchemaType& GetRoot() const { return *root_; }
@@ -1611,12 +1723,7 @@ private:
1611
1723
  //! Prohibit assignment
1612
1724
  GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1613
1725
 
1614
- struct SchemaRefEntry {
1615
- SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1616
- PointerType source;
1617
- PointerType target;
1618
- const SchemaType** schema;
1619
- };
1726
+ typedef const PointerType* SchemaRefPtr; // PR #1393
1620
1727
 
1621
1728
  struct SchemaEntry {
1622
1729
  SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
@@ -1631,79 +1738,197 @@ private:
1631
1738
  bool owned;
1632
1739
  };
1633
1740
 
1634
- void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1635
- if (schema)
1636
- *schema = typeless_;
1637
-
1741
+ // Changed by PR #1393
1742
+ void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
1638
1743
  if (v.GetType() == kObjectType) {
1639
- const SchemaType* s = GetSchema(pointer);
1640
- if (!s)
1641
- CreateSchema(schema, pointer, v, document);
1744
+ UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_);
1642
1745
 
1643
1746
  for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1644
- CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1747
+ CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
1645
1748
  }
1646
1749
  else if (v.GetType() == kArrayType)
1647
1750
  for (SizeType i = 0; i < v.Size(); i++)
1648
- CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1751
+ CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id);
1649
1752
  }
1650
1753
 
1651
- void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1754
+ // Changed by PR #1393
1755
+ const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
1652
1756
  RAPIDJSON_ASSERT(pointer.IsValid());
1653
1757
  if (v.IsObject()) {
1654
- if (!HandleRefSchema(pointer, schema, v, document)) {
1655
- SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1656
- new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1758
+ if (const SchemaType* sc = GetSchema(pointer)) {
1759
+ if (schema)
1760
+ *schema = sc;
1761
+ AddSchemaRefs(const_cast<SchemaType*>(sc));
1762
+ }
1763
+ else if (!HandleRefSchema(pointer, schema, v, document, id)) {
1764
+ // The new schema constructor adds itself and its $ref(s) to schemaMap_
1765
+ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
1657
1766
  if (schema)
1658
1767
  *schema = s;
1768
+ return s->GetId();
1659
1769
  }
1660
1770
  }
1771
+ else {
1772
+ if (schema)
1773
+ *schema = typeless_;
1774
+ AddSchemaRefs(typeless_);
1775
+ }
1776
+ return id;
1661
1777
  }
1662
1778
 
1663
- bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1664
- static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1665
- static const ValueType kRefValue(kRefString, 4);
1666
-
1667
- typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1779
+ // Changed by PR #1393
1780
+ // TODO should this return a UriType& ?
1781
+ bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
1782
+ typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
1668
1783
  if (itr == v.MemberEnd())
1669
1784
  return false;
1670
1785
 
1786
+ // Resolve the source pointer to the $ref'ed schema (finally)
1787
+ new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
1788
+
1671
1789
  if (itr->value.IsString()) {
1672
1790
  SizeType len = itr->value.GetStringLength();
1673
1791
  if (len > 0) {
1674
- const Ch* s = itr->value.GetString();
1675
- SizeType i = 0;
1676
- while (i < len && s[i] != '#') // Find the first #
1677
- i++;
1678
-
1679
- if (i > 0) { // Remote reference, resolve immediately
1792
+ // First resolve $ref against the in-scope id
1793
+ UriType scopeId = UriType(id, allocator_);
1794
+ UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
1795
+ // See if the resolved $ref minus the fragment matches a resolved id in this document
1796
+ // Search from the root. Returns the subschema in the document and its absolute JSON pointer.
1797
+ PointerType basePointer = PointerType();
1798
+ const ValueType *base = FindId(document, ref, basePointer, docId_, false);
1799
+ if (!base) {
1800
+ // Remote reference - call the remote document provider
1680
1801
  if (remoteProvider_) {
1681
- if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
1682
- PointerType pointer(&s[i], len - i, allocator_);
1683
- if (pointer.IsValid()) {
1684
- if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1685
- if (schema)
1686
- *schema = sc;
1687
- new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(source, const_cast<SchemaType*>(sc), false, allocator_);
1802
+ if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) {
1803
+ const Ch* s = ref.GetFragString();
1804
+ len = ref.GetFragStringLength();
1805
+ if (len <= 1 || s[1] == '/') {
1806
+ // JSON pointer fragment, absolute in the remote schema
1807
+ const PointerType pointer(s, len, allocator_);
1808
+ if (pointer.IsValid()) {
1809
+ // Get the subschema
1810
+ if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
1811
+ if (schema)
1812
+ *schema = sc;
1813
+ AddSchemaRefs(const_cast<SchemaType *>(sc));
1814
+ return true;
1815
+ }
1816
+ }
1817
+ } else {
1818
+ // Plain name fragment, not allowed
1819
+ }
1820
+ }
1821
+ }
1822
+ }
1823
+ else { // Local reference
1824
+ const Ch* s = ref.GetFragString();
1825
+ len = ref.GetFragStringLength();
1826
+ if (len <= 1 || s[1] == '/') {
1827
+ // JSON pointer fragment, relative to the resolved URI
1828
+ const PointerType relPointer(s, len, allocator_);
1829
+ if (relPointer.IsValid()) {
1830
+ // Get the subschema
1831
+ if (const ValueType *pv = relPointer.Get(*base)) {
1832
+ // Now get the absolute JSON pointer by adding relative to base
1833
+ PointerType pointer(basePointer);
1834
+ for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
1835
+ pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
1836
+ //GenericStringBuffer<EncodingType> sb;
1837
+ //pointer.StringifyUriFragment(sb);
1838
+ if (pointer.IsValid() && !IsCyclicRef(pointer)) {
1839
+ // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
1840
+ // TODO: cache pointer <-> id mapping
1841
+ size_t unresolvedTokenIndex;
1842
+ scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
1843
+ CreateSchema(schema, pointer, *pv, document, scopeId);
1688
1844
  return true;
1689
1845
  }
1690
1846
  }
1691
1847
  }
1848
+ } else {
1849
+ // Plain name fragment, relative to the resolved URI
1850
+ // See if the fragment matches an id in this document.
1851
+ // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
1852
+ PointerType pointer = PointerType();
1853
+ if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
1854
+ if (!IsCyclicRef(pointer)) {
1855
+ //GenericStringBuffer<EncodingType> sb;
1856
+ //pointer.StringifyUriFragment(sb);
1857
+ // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
1858
+ // TODO: cache pointer <-> id mapping
1859
+ size_t unresolvedTokenIndex;
1860
+ scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
1861
+ CreateSchema(schema, pointer, *pv, document, scopeId);
1862
+ return true;
1863
+ }
1864
+ }
1692
1865
  }
1693
1866
  }
1694
- else if (s[i] == '#') { // Local reference, defer resolution
1695
- PointerType pointer(&s[i], len - i, allocator_);
1696
- if (pointer.IsValid()) {
1697
- if (const ValueType* nv = pointer.Get(document))
1698
- if (HandleRefSchema(source, schema, *nv, document))
1699
- return true;
1867
+ }
1868
+ }
1700
1869
 
1701
- new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1702
- return true;
1703
- }
1870
+ // Invalid/Unknown $ref
1871
+ if (schema)
1872
+ *schema = typeless_;
1873
+ AddSchemaRefs(typeless_);
1874
+ return true;
1875
+ }
1876
+
1877
+ //! Find the first subschema with a resolved 'id' that matches the specified URI.
1878
+ // If full specified use all URI else ignore fragment.
1879
+ // If found, return a pointer to the subschema and its JSON pointer.
1880
+ // TODO cache pointer <-> id mapping
1881
+ ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const {
1882
+ SizeType i = 0;
1883
+ ValueType* resval = 0;
1884
+ UriType tempuri = UriType(finduri, allocator_);
1885
+ UriType localuri = UriType(baseuri, allocator_);
1886
+ if (doc.GetType() == kObjectType) {
1887
+ // Establish the base URI of this object
1888
+ typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
1889
+ if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
1890
+ localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
1891
+ }
1892
+ // See if it matches
1893
+ if (localuri.Match(finduri, full)) {
1894
+ resval = const_cast<ValueType *>(&doc);
1895
+ resptr = here;
1896
+ return resval;
1897
+ }
1898
+ // No match, continue looking
1899
+ for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
1900
+ if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
1901
+ resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
1704
1902
  }
1903
+ if (resval) break;
1904
+ }
1905
+ } else if (doc.GetType() == kArrayType) {
1906
+ // Continue looking
1907
+ for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
1908
+ if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
1909
+ resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
1910
+ }
1911
+ if (resval) break;
1912
+ i++;
1705
1913
  }
1706
1914
  }
1915
+ return resval;
1916
+ }
1917
+
1918
+ // Added by PR #1393
1919
+ void AddSchemaRefs(SchemaType* schema) {
1920
+ while (!schemaRef_.Empty()) {
1921
+ SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
1922
+ SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
1923
+ new (entry) SchemaEntry(**ref, schema, false, allocator_);
1924
+ }
1925
+ }
1926
+
1927
+ // Added by PR #1393
1928
+ bool IsCyclicRef(const PointerType& pointer) const {
1929
+ for (const SchemaRefPtr* ref = schemaRef_.template Bottom<SchemaRefPtr>(); ref != schemaRef_.template End<SchemaRefPtr>(); ++ref)
1930
+ if (pointer == **ref)
1931
+ return true;
1707
1932
  return false;
1708
1933
  }
1709
1934
 
@@ -1732,8 +1957,9 @@ private:
1732
1957
  const SchemaType* root_; //!< Root schema.
1733
1958
  SchemaType* typeless_;
1734
1959
  internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
1735
- internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
1736
- URIType uri_;
1960
+ internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
1961
+ SValue uri_; // Schema document URI
1962
+ UriType docId_;
1737
1963
  };
1738
1964
 
1739
1965
  //! GenericSchemaDocument using Value type.
@@ -1763,8 +1989,7 @@ template <
1763
1989
  class GenericSchemaValidator :
1764
1990
  public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1765
1991
  public internal::ISchemaValidator,
1766
- public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType>
1767
- {
1992
+ public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
1768
1993
  public:
1769
1994
  typedef typename SchemaDocumentType::SchemaType SchemaType;
1770
1995
  typedef typename SchemaDocumentType::PointerType PointerType;
@@ -1797,7 +2022,8 @@ public:
1797
2022
  error_(kObjectType),
1798
2023
  currentError_(),
1799
2024
  missingDependents_(),
1800
- valid_(true)
2025
+ valid_(true),
2026
+ flags_(kValidateDefaultFlags)
1801
2027
  #if RAPIDJSON_SCHEMA_VERBOSE
1802
2028
  , depth_(0)
1803
2029
  #endif
@@ -1828,7 +2054,8 @@ public:
1828
2054
  error_(kObjectType),
1829
2055
  currentError_(),
1830
2056
  missingDependents_(),
1831
- valid_(true)
2057
+ valid_(true),
2058
+ flags_(kValidateDefaultFlags)
1832
2059
  #if RAPIDJSON_SCHEMA_VERBOSE
1833
2060
  , depth_(0)
1834
2061
  #endif
@@ -1846,31 +2073,61 @@ public:
1846
2073
  while (!schemaStack_.Empty())
1847
2074
  PopSchema();
1848
2075
  documentStack_.Clear();
2076
+ ResetError();
2077
+ }
2078
+
2079
+ //! Reset the error state.
2080
+ void ResetError() {
1849
2081
  error_.SetObject();
1850
2082
  currentError_.SetNull();
1851
2083
  missingDependents_.SetNull();
1852
2084
  valid_ = true;
1853
2085
  }
1854
2086
 
2087
+ //! Implementation of ISchemaValidator
2088
+ void SetValidateFlags(unsigned flags) {
2089
+ flags_ = flags;
2090
+ }
2091
+ virtual unsigned GetValidateFlags() const {
2092
+ return flags_;
2093
+ }
2094
+
1855
2095
  //! Checks whether the current state is valid.
1856
2096
  // Implementation of ISchemaValidator
1857
- virtual bool IsValid() const { return valid_; }
2097
+ virtual bool IsValid() const {
2098
+ if (!valid_) return false;
2099
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
2100
+ return true;
2101
+ }
1858
2102
 
1859
2103
  //! Gets the error object.
1860
2104
  ValueType& GetError() { return error_; }
1861
2105
  const ValueType& GetError() const { return error_; }
1862
2106
 
1863
2107
  //! Gets the JSON pointer pointed to the invalid schema.
2108
+ // If reporting all errors, the stack will be empty.
1864
2109
  PointerType GetInvalidSchemaPointer() const {
1865
2110
  return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
1866
2111
  }
1867
2112
 
1868
2113
  //! Gets the keyword of invalid schema.
2114
+ // If reporting all errors, the stack will be empty, so return "errors".
1869
2115
  const Ch* GetInvalidSchemaKeyword() const {
1870
- return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
2116
+ if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
2117
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString();
2118
+ return 0;
2119
+ }
2120
+
2121
+ //! Gets the error code of invalid schema.
2122
+ // If reporting all errors, the stack will be empty, so return kValidateErrors.
2123
+ ValidateErrorCode GetInvalidSchemaCode() const {
2124
+ if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
2125
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
2126
+ return kValidateErrorNone;
1871
2127
  }
1872
2128
 
1873
2129
  //! Gets the JSON pointer pointed to the invalid value.
2130
+ // If reporting all errors, the stack will be empty.
1874
2131
  PointerType GetInvalidDocumentPointer() const {
1875
2132
  if (documentStack_.Empty()) {
1876
2133
  return PointerType();
@@ -1881,64 +2138,64 @@ public:
1881
2138
  }
1882
2139
 
1883
2140
  void NotMultipleOf(int64_t actual, const SValue& expected) {
1884
- AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
2141
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
1885
2142
  }
1886
2143
  void NotMultipleOf(uint64_t actual, const SValue& expected) {
1887
- AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
2144
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
1888
2145
  }
1889
2146
  void NotMultipleOf(double actual, const SValue& expected) {
1890
- AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
2147
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
1891
2148
  }
1892
2149
  void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
1893
- AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
2150
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
1894
2151
  exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
1895
2152
  }
1896
2153
  void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
1897
- AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
2154
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
1898
2155
  exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
1899
2156
  }
1900
2157
  void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
1901
- AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
2158
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
1902
2159
  exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
1903
2160
  }
1904
2161
  void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
1905
- AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
2162
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
1906
2163
  exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
1907
2164
  }
1908
2165
  void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
1909
- AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
2166
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
1910
2167
  exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
1911
2168
  }
1912
2169
  void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
1913
- AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
2170
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
1914
2171
  exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
1915
2172
  }
1916
2173
 
1917
2174
  void TooLong(const Ch* str, SizeType length, SizeType expected) {
1918
- AddNumberError(SchemaType::GetMaxLengthString(),
2175
+ AddNumberError(kValidateErrorMaxLength,
1919
2176
  ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
1920
2177
  }
1921
2178
  void TooShort(const Ch* str, SizeType length, SizeType expected) {
1922
- AddNumberError(SchemaType::GetMinLengthString(),
2179
+ AddNumberError(kValidateErrorMinLength,
1923
2180
  ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
1924
2181
  }
1925
2182
  void DoesNotMatch(const Ch* str, SizeType length) {
1926
2183
  currentError_.SetObject();
1927
2184
  currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
1928
- AddCurrentError(SchemaType::GetPatternString());
2185
+ AddCurrentError(kValidateErrorPattern);
1929
2186
  }
1930
2187
 
1931
2188
  void DisallowedItem(SizeType index) {
1932
2189
  currentError_.SetObject();
1933
2190
  currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
1934
- AddCurrentError(SchemaType::GetAdditionalItemsString(), true);
2191
+ AddCurrentError(kValidateErrorAdditionalItems, true);
1935
2192
  }
1936
2193
  void TooFewItems(SizeType actualCount, SizeType expectedCount) {
1937
- AddNumberError(SchemaType::GetMinItemsString(),
2194
+ AddNumberError(kValidateErrorMinItems,
1938
2195
  ValueType(actualCount).Move(), SValue(expectedCount).Move());
1939
2196
  }
1940
2197
  void TooManyItems(SizeType actualCount, SizeType expectedCount) {
1941
- AddNumberError(SchemaType::GetMaxItemsString(),
2198
+ AddNumberError(kValidateErrorMaxItems,
1942
2199
  ValueType(actualCount).Move(), SValue(expectedCount).Move());
1943
2200
  }
1944
2201
  void DuplicateItems(SizeType index1, SizeType index2) {
@@ -1947,15 +2204,15 @@ public:
1947
2204
  duplicates.PushBack(index2, GetStateAllocator());
1948
2205
  currentError_.SetObject();
1949
2206
  currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
1950
- AddCurrentError(SchemaType::GetUniqueItemsString(), true);
2207
+ AddCurrentError(kValidateErrorUniqueItems, true);
1951
2208
  }
1952
2209
 
1953
2210
  void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
1954
- AddNumberError(SchemaType::GetMaxPropertiesString(),
2211
+ AddNumberError(kValidateErrorMaxProperties,
1955
2212
  ValueType(actualCount).Move(), SValue(expectedCount).Move());
1956
2213
  }
1957
2214
  void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
1958
- AddNumberError(SchemaType::GetMinPropertiesString(),
2215
+ AddNumberError(kValidateErrorMinProperties,
1959
2216
  ValueType(actualCount).Move(), SValue(expectedCount).Move());
1960
2217
  }
1961
2218
  void StartMissingProperties() {
@@ -1970,7 +2227,7 @@ public:
1970
2227
  ValueType error(kObjectType);
1971
2228
  error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
1972
2229
  currentError_ = error;
1973
- AddCurrentError(SchemaType::GetRequiredString());
2230
+ AddCurrentError(kValidateErrorRequired);
1974
2231
  return true;
1975
2232
  }
1976
2233
  void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
@@ -1980,7 +2237,7 @@ public:
1980
2237
  void DisallowedProperty(const Ch* name, SizeType length) {
1981
2238
  currentError_.SetObject();
1982
2239
  currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
1983
- AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true);
2240
+ AddCurrentError(kValidateErrorAdditionalProperties, true);
1984
2241
  }
1985
2242
 
1986
2243
  void StartDependencyErrors() {
@@ -1993,9 +2250,20 @@ public:
1993
2250
  missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
1994
2251
  }
1995
2252
  void EndMissingDependentProperties(const SValue& sourceName) {
1996
- if (!missingDependents_.Empty())
1997
- currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
1998
- missingDependents_, GetStateAllocator());
2253
+ if (!missingDependents_.Empty()) {
2254
+ // Create equivalent 'required' error
2255
+ ValueType error(kObjectType);
2256
+ ValidateErrorCode code = kValidateErrorRequired;
2257
+ error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
2258
+ AddErrorCode(error, code);
2259
+ AddErrorInstanceLocation(error, false);
2260
+ // When appending to a pointer ensure its allocator is used
2261
+ PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
2262
+ AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
2263
+ ValueType wrapper(kObjectType);
2264
+ wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
2265
+ currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
2266
+ }
1999
2267
  }
2000
2268
  void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
2001
2269
  currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
@@ -2007,13 +2275,13 @@ public:
2007
2275
  ValueType error(kObjectType);
2008
2276
  error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
2009
2277
  currentError_ = error;
2010
- AddCurrentError(SchemaType::GetDependenciesString());
2278
+ AddCurrentError(kValidateErrorDependencies);
2011
2279
  return true;
2012
2280
  }
2013
2281
 
2014
- void DisallowedValue() {
2282
+ void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
2015
2283
  currentError_.SetObject();
2016
- AddCurrentError(SchemaType::GetEnumString());
2284
+ AddCurrentError(code);
2017
2285
  }
2018
2286
  void StartDisallowedType() {
2019
2287
  currentError_.SetArray();
@@ -2026,22 +2294,24 @@ public:
2026
2294
  error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
2027
2295
  error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
2028
2296
  currentError_ = error;
2029
- AddCurrentError(SchemaType::GetTypeString());
2297
+ AddCurrentError(kValidateErrorType);
2030
2298
  }
2031
2299
  void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
2032
- for (SizeType i = 0; i < count; ++i) {
2033
- MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2034
- }
2300
+ // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
2301
+ AddErrorArray(kValidateErrorAllOf, subvalidators, count);
2302
+ //for (SizeType i = 0; i < count; ++i) {
2303
+ // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
2304
+ //}
2035
2305
  }
2036
2306
  void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
2037
- AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count);
2307
+ AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
2038
2308
  }
2039
- void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
2040
- AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count);
2309
+ void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) {
2310
+ AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count);
2041
2311
  }
2042
2312
  void Disallowed() {
2043
2313
  currentError_.SetObject();
2044
- AddCurrentError(SchemaType::GetNotString());
2314
+ AddCurrentError(kValidateErrorNot);
2045
2315
  }
2046
2316
 
2047
2317
  #define RAPIDJSON_STRING_(name, ...) \
@@ -2058,6 +2328,8 @@ public:
2058
2328
  RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
2059
2329
  RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
2060
2330
  RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
2331
+ RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
2332
+ RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
2061
2333
  RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
2062
2334
 
2063
2335
  #undef RAPIDJSON_STRING_
@@ -2075,7 +2347,7 @@ RAPIDJSON_MULTILINEMACRO_END
2075
2347
 
2076
2348
  #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
2077
2349
  if (!valid_) return false; \
2078
- if (!BeginValue() || !CurrentSchema().method arg1) {\
2350
+ if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
2079
2351
  RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
2080
2352
  return valid_ = false;\
2081
2353
  }
@@ -2093,7 +2365,8 @@ RAPIDJSON_MULTILINEMACRO_END
2093
2365
  }
2094
2366
 
2095
2367
  #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
2096
- return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
2368
+ valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
2369
+ return valid_;
2097
2370
 
2098
2371
  #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
2099
2372
  RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
@@ -2121,15 +2394,15 @@ RAPIDJSON_MULTILINEMACRO_END
2121
2394
  bool Key(const Ch* str, SizeType len, bool copy) {
2122
2395
  if (!valid_) return false;
2123
2396
  AppendToken(str, len);
2124
- if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
2397
+ if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false;
2125
2398
  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
2126
2399
  return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
2127
2400
  }
2128
2401
 
2129
- bool EndObject(SizeType memberCount) {
2402
+ bool EndObject(SizeType memberCount) {
2130
2403
  if (!valid_) return false;
2131
2404
  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
2132
- if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
2405
+ if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false;
2133
2406
  RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
2134
2407
  }
2135
2408
 
@@ -2142,7 +2415,7 @@ RAPIDJSON_MULTILINEMACRO_END
2142
2415
  bool EndArray(SizeType elementCount) {
2143
2416
  if (!valid_) return false;
2144
2417
  RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
2145
- if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
2418
+ if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false;
2146
2419
  RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
2147
2420
  }
2148
2421
 
@@ -2152,12 +2425,14 @@ RAPIDJSON_MULTILINEMACRO_END
2152
2425
  #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
2153
2426
 
2154
2427
  // Implementation of ISchemaStateFactory<SchemaType>
2155
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
2156
- return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
2428
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
2429
+ ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
2157
2430
  #if RAPIDJSON_SCHEMA_VERBOSE
2158
2431
  depth_ + 1,
2159
2432
  #endif
2160
2433
  &GetStateAllocator());
2434
+ sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag);
2435
+ return sv;
2161
2436
  }
2162
2437
 
2163
2438
  virtual void DestroySchemaValidator(ISchemaValidator* validator) {
@@ -2214,7 +2489,8 @@ private:
2214
2489
  error_(kObjectType),
2215
2490
  currentError_(),
2216
2491
  missingDependents_(),
2217
- valid_(true)
2492
+ valid_(true),
2493
+ flags_(kValidateDefaultFlags)
2218
2494
  #if RAPIDJSON_SCHEMA_VERBOSE
2219
2495
  , depth_(depth)
2220
2496
  #endif
@@ -2229,6 +2505,10 @@ private:
2229
2505
  return *stateAllocator_;
2230
2506
  }
2231
2507
 
2508
+ bool GetContinueOnErrors() const {
2509
+ return flags_ & kValidateContinueOnErrorFlag;
2510
+ }
2511
+
2232
2512
  bool BeginValue() {
2233
2513
  if (schemaStack_.Empty())
2234
2514
  PushSchema(root_);
@@ -2236,7 +2516,7 @@ private:
2236
2516
  if (CurrentContext().inArray)
2237
2517
  internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
2238
2518
 
2239
- if (!CurrentSchema().BeginValue(CurrentContext()))
2519
+ if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
2240
2520
  return false;
2241
2521
 
2242
2522
  SizeType count = CurrentContext().patternPropertiesSchemaCount;
@@ -2252,7 +2532,7 @@ private:
2252
2532
  SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
2253
2533
  va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
2254
2534
  for (SizeType i = 0; i < count; i++)
2255
- va[validatorCount++] = CreateSchemaValidator(*sa[i]);
2535
+ va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
2256
2536
  }
2257
2537
 
2258
2538
  CurrentContext().arrayUniqueness = valueUniqueness;
@@ -2261,7 +2541,7 @@ private:
2261
2541
  }
2262
2542
 
2263
2543
  bool EndValue() {
2264
- if (!CurrentSchema().EndValue(CurrentContext()))
2544
+ if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
2265
2545
  return false;
2266
2546
 
2267
2547
  #if RAPIDJSON_SCHEMA_VERBOSE
@@ -2272,21 +2552,27 @@ private:
2272
2552
  documentStack_.template Pop<Ch>(1);
2273
2553
  internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
2274
2554
  #endif
2275
-
2276
- uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
2555
+ void* hasher = CurrentContext().hasher;
2556
+ uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
2277
2557
 
2278
2558
  PopSchema();
2279
2559
 
2280
2560
  if (!schemaStack_.Empty()) {
2281
2561
  Context& context = CurrentContext();
2282
- if (context.valueUniqueness) {
2562
+ // Only check uniqueness if there is a hasher
2563
+ if (hasher && context.valueUniqueness) {
2283
2564
  HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
2284
2565
  if (!a)
2285
2566
  CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
2286
2567
  for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
2287
2568
  if (itr->GetUint64() == h) {
2288
2569
  DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
2289
- RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
2570
+ // Cleanup before returning if continuing
2571
+ if (GetContinueOnErrors()) {
2572
+ a->PushBack(h, GetStateAllocator());
2573
+ while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
2574
+ }
2575
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
2290
2576
  }
2291
2577
  a->PushBack(h, GetStateAllocator());
2292
2578
  }
@@ -2327,25 +2613,32 @@ private:
2327
2613
  c->~Context();
2328
2614
  }
2329
2615
 
2330
- void AddErrorLocation(ValueType& result, bool parent) {
2616
+ void AddErrorInstanceLocation(ValueType& result, bool parent) {
2331
2617
  GenericStringBuffer<EncodingType> sb;
2332
2618
  PointerType instancePointer = GetInvalidDocumentPointer();
2333
2619
  ((parent && instancePointer.GetTokenCount() > 0)
2334
- ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
2335
- : instancePointer).StringifyUriFragment(sb);
2620
+ ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
2621
+ : instancePointer).StringifyUriFragment(sb);
2336
2622
  ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
2337
- GetStateAllocator());
2623
+ GetStateAllocator());
2338
2624
  result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
2339
- sb.Clear();
2340
- memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()),
2341
- CurrentSchema().GetURI().GetString(),
2342
- CurrentSchema().GetURI().GetStringLength() * sizeof(Ch));
2343
- GetInvalidSchemaPointer().StringifyUriFragment(sb);
2625
+ }
2626
+
2627
+ void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
2628
+ GenericStringBuffer<EncodingType> sb;
2629
+ SizeType len = CurrentSchema().GetURI().GetStringLength();
2630
+ if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
2631
+ if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
2632
+ else GetInvalidSchemaPointer().StringifyUriFragment(sb);
2344
2633
  ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
2345
2634
  GetStateAllocator());
2346
2635
  result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
2347
2636
  }
2348
2637
 
2638
+ void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
2639
+ result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
2640
+ }
2641
+
2349
2642
  void AddError(ValueType& keyword, ValueType& error) {
2350
2643
  typename ValueType::MemberIterator member = error_.FindMember(keyword);
2351
2644
  if (member == error_.MemberEnd())
@@ -2360,9 +2653,11 @@ private:
2360
2653
  }
2361
2654
  }
2362
2655
 
2363
- void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) {
2364
- AddErrorLocation(currentError_, parent);
2365
- AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_);
2656
+ void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
2657
+ AddErrorCode(currentError_, code);
2658
+ AddErrorInstanceLocation(currentError_, parent);
2659
+ AddErrorSchemaLocation(currentError_);
2660
+ AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
2366
2661
  }
2367
2662
 
2368
2663
  void MergeError(ValueType& other) {
@@ -2371,24 +2666,24 @@ private:
2371
2666
  }
2372
2667
  }
2373
2668
 
2374
- void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected,
2669
+ void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
2375
2670
  const typename SchemaType::ValueType& (*exclusive)() = 0) {
2376
2671
  currentError_.SetObject();
2377
2672
  currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
2378
2673
  currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
2379
2674
  if (exclusive)
2380
2675
  currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
2381
- AddCurrentError(keyword);
2676
+ AddCurrentError(code);
2382
2677
  }
2383
2678
 
2384
- void AddErrorArray(const typename SchemaType::ValueType& keyword,
2679
+ void AddErrorArray(const ValidateErrorCode code,
2385
2680
  ISchemaValidator** subvalidators, SizeType count) {
2386
2681
  ValueType errors(kArrayType);
2387
2682
  for (SizeType i = 0; i < count; ++i)
2388
2683
  errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
2389
2684
  currentError_.SetObject();
2390
2685
  currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
2391
- AddCurrentError(keyword);
2686
+ AddCurrentError(code);
2392
2687
  }
2393
2688
 
2394
2689
  const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
@@ -2408,6 +2703,7 @@ private:
2408
2703
  ValueType currentError_;
2409
2704
  ValueType missingDependents_;
2410
2705
  bool valid_;
2706
+ unsigned flags_;
2411
2707
  #if RAPIDJSON_SCHEMA_VERBOSE
2412
2708
  unsigned depth_;
2413
2709
  #endif
@@ -2445,7 +2741,7 @@ public:
2445
2741
  \param is Input stream.
2446
2742
  \param sd Schema document.
2447
2743
  */
2448
- SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {}
2744
+ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
2449
2745
 
2450
2746
  template <typename Handler>
2451
2747
  bool operator()(Handler& handler) {
@@ -2463,6 +2759,7 @@ public:
2463
2759
  else {
2464
2760
  invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
2465
2761
  invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
2762
+ invalidSchemaCode_ = validator.GetInvalidSchemaCode();
2466
2763
  invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
2467
2764
  error_.CopyFrom(validator.GetError(), allocator_);
2468
2765
  }
@@ -2476,6 +2773,7 @@ public:
2476
2773
  const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
2477
2774
  const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
2478
2775
  const ValueType& GetError() const { return error_; }
2776
+ ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
2479
2777
 
2480
2778
  private:
2481
2779
  InputStream& is_;
@@ -2485,6 +2783,7 @@ private:
2485
2783
  PointerType invalidSchemaPointer_;
2486
2784
  const Ch* invalidSchemaKeyword_;
2487
2785
  PointerType invalidDocumentPointer_;
2786
+ ValidateErrorCode invalidSchemaCode_;
2488
2787
  StackAllocator allocator_;
2489
2788
  ValueType error_;
2490
2789
  bool isValid_;