ext/rj_schema/rapidjson/include/rapidjson/schema.h in rj_schema-0.2.6 vs ext/rj_schema/rapidjson/include/rapidjson/schema.h in rj_schema-1.0.0
- old
+ new
@@ -16,10 +16,11 @@
#define RAPIDJSON_SCHEMA_H_
#include "document.h"
#include "pointer.h"
#include "stringbuffer.h"
+#include "error/en.h"
#include <cmath> // abs, floor
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
#else
@@ -111,18 +112,41 @@
#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
#else
#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
#endif
-#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
RAPIDJSON_MULTILINEMACRO_BEGIN\
- context.invalidKeyword = keyword.GetString();\
- RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
+ context.invalidCode = code;\
+ context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
+ RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\
return false;\
RAPIDJSON_MULTILINEMACRO_END
///////////////////////////////////////////////////////////////////////////////
+// ValidateFlag
+
+/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kValidateDefaultFlags definition.
+
+ User can define this as any \c ValidateFlag combinations.
+*/
+#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
+#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
+#endif
+
+//! Combination of validate flags
+/*! \see
+ */
+enum ValidateFlag {
+ kValidateNoFlags = 0, //!< No flags are set.
+ kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
+ kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
+};
+
+///////////////////////////////////////////////////////////////////////////////
// Forward declarations
template <typename ValueType, typename Allocator>
class GenericSchemaDocument;
@@ -136,20 +160,22 @@
class ISchemaValidator {
public:
virtual ~ISchemaValidator() {}
virtual bool IsValid() const = 0;
+ virtual void SetValidateFlags(unsigned flags) = 0;
+ virtual unsigned GetValidateFlags() const = 0;
};
///////////////////////////////////////////////////////////////////////////////
// ISchemaStateFactory
template <typename SchemaType>
class ISchemaStateFactory {
public:
virtual ~ISchemaStateFactory() {}
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
virtual void* CreateHasher() = 0;
virtual uint64_t GetHashCode(void* hasher) = 0;
virtual void DestroryHasher(void* hasher) = 0;
virtual void* MallocState(size_t size) = 0;
@@ -199,17 +225,17 @@
virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
virtual bool EndDependencyErrors() = 0;
- virtual void DisallowedValue() = 0;
+ virtual void DisallowedValue(const ValidateErrorCode code) = 0;
virtual void StartDisallowedType() = 0;
virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
- virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
+ virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0;
virtual void Disallowed() = 0;
};
///////////////////////////////////////////////////////////////////////////////
@@ -330,10 +356,11 @@
factory(f),
error_handler(eh),
schema(s),
valueSchema(),
invalidKeyword(),
+ invalidCode(),
hasher(),
arrayElementHashCodes(),
validators(),
validatorCount(),
patternPropertiesValidators(),
@@ -370,10 +397,11 @@
SchemaValidatorFactoryType& factory;
ErrorHandlerType& error_handler;
const SchemaType* schema;
const SchemaType* valueSchema;
const Ch* invalidKeyword;
+ ValidateErrorCode invalidCode;
void* hasher; // Only validator access
void* arrayElementHashCodes; // Only validator access this
ISchemaValidator** validators;
SizeType validatorCount;
ISchemaValidator** patternPropertiesValidators;
@@ -456,11 +484,11 @@
else if (v->IsArray())
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
AddType(*itr);
}
- if (const ValueType* v = GetMember(value, GetEnumString()))
+ if (const ValueType* v = GetMember(value, GetEnumString())) {
if (v->IsArray() && v->Size() > 0) {
enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
char buffer[256u + 24];
@@ -468,10 +496,11 @@
EnumHasherType h(&hasherAllocator, 256);
itr->Accept(h);
enum_[enumCount_++] = h.GetHashCode();
}
}
+ }
if (schemaDocument) {
AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
@@ -686,11 +715,15 @@
context.valueSchema = additionalItemsSchema_;
else if (additionalItems_)
context.valueSchema = typeless_;
else {
context.error_handler.DisallowedItem(context.arrayElementIndex);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
+ // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
+ context.valueSchema = typeless_;
+ // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
+ context.arrayElementIndex++;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
}
}
else
context.valueSchema = typeless_;
@@ -698,10 +731,11 @@
}
return true;
}
RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
+ // Only check pattern properties if we have validators
if (context.patternPropertiesValidatorCount > 0) {
bool otherValid = false;
SizeType count = context.patternPropertiesValidatorCount;
if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
otherValid = context.patternPropertiesValidators[--count]->IsValid();
@@ -714,87 +748,91 @@
}
if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
if (!patternValid) {
context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
}
}
else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
if (!patternValid || !otherValid) {
context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
}
}
else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
}
}
- if (enum_) {
+ // For enums only check if we have a hasher
+ if (enum_ && context.hasher) {
const uint64_t h = context.factory.GetHashCode(context.hasher);
for (SizeType i = 0; i < enumCount_; i++)
if (enum_[i] == h)
goto foundEnum;
- context.error_handler.DisallowedValue();
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
+ context.error_handler.DisallowedValue(kValidateErrorEnum);
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
foundEnum:;
}
- if (allOf_.schemas)
- for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
- if (!context.validators[i]->IsValid()) {
- context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
- }
-
- if (anyOf_.schemas) {
- for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
- if (context.validators[i]->IsValid())
- goto foundAny;
- context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
- foundAny:;
- }
+ // Only check allOf etc if we have validators
+ if (context.validatorCount > 0) {
+ if (allOf_.schemas)
+ for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
+ if (!context.validators[i]->IsValid()) {
+ context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
+ }
- if (oneOf_.schemas) {
- bool oneValid = false;
- for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
- if (context.validators[i]->IsValid()) {
- if (oneValid) {
- context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
- } else
- oneValid = true;
+ if (anyOf_.schemas) {
+ for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
+ if (context.validators[i]->IsValid())
+ goto foundAny;
+ context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
+ foundAny:;
+ }
+
+ if (oneOf_.schemas) {
+ bool oneValid = false;
+ for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
+ if (context.validators[i]->IsValid()) {
+ if (oneValid) {
+ context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true);
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
+ } else
+ oneValid = true;
+ }
+ if (!oneValid) {
+ context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false);
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
}
- if (!oneValid) {
- context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
}
- }
- if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
- context.error_handler.Disallowed();
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
+ if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
+ context.error_handler.Disallowed();
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
+ }
}
return true;
}
bool Null(Context& context) const {
if (!(type_ & (1 << kNullSchemaType))) {
DisallowedType(context, GetNullString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
return CreateParallelValidator(context);
}
bool Bool(Context& context, bool) const {
if (!(type_ & (1 << kBooleanSchemaType))) {
DisallowedType(context, GetBooleanString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
return CreateParallelValidator(context);
}
bool Int(Context& context, int i) const {
@@ -822,11 +860,11 @@
}
bool Double(Context& context, double d) const {
if (!(type_ & (1 << kNumberSchemaType))) {
DisallowedType(context, GetNumberString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
return false;
@@ -840,39 +878,39 @@
}
bool String(Context& context, const Ch* str, SizeType length, bool) const {
if (!(type_ & (1 << kStringSchemaType))) {
DisallowedType(context, GetStringString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
SizeType count;
if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
if (count < minLength_) {
context.error_handler.TooShort(str, length, minLength_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
}
if (count > maxLength_) {
context.error_handler.TooLong(str, length, maxLength_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
}
}
}
if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
context.error_handler.DoesNotMatch(str, length);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
}
return CreateParallelValidator(context);
}
bool StartObject(Context& context) const {
if (!(type_ & (1 << kObjectSchemaType))) {
DisallowedType(context, GetObjectString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
if (hasDependencies_ || hasRequired_) {
context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
@@ -928,12 +966,14 @@
context.valueSchema = typeless_;
return true;
}
if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
+ // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
+ context.valueSchema = typeless_;
context.error_handler.DisallowedProperty(str, len);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
}
return true;
}
@@ -943,21 +983,21 @@
for (SizeType index = 0; index < propertyCount_; index++)
if (properties_[index].required && !context.propertyExist[index])
if (properties_[index].schema->defaultValueLength_ == 0 )
context.error_handler.AddMissingProperty(properties_[index].name);
if (context.error_handler.EndMissingProperties())
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
}
if (memberCount < minProperties_) {
context.error_handler.TooFewProperties(memberCount, minProperties_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
}
if (memberCount > maxProperties_) {
context.error_handler.TooManyProperties(memberCount, maxProperties_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
}
if (hasDependencies_) {
context.error_handler.StartDependencyErrors();
for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
@@ -976,44 +1016,82 @@
context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
}
}
}
if (context.error_handler.EndDependencyErrors())
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
}
return true;
}
bool StartArray(Context& context) const {
+ context.arrayElementIndex = 0;
+ context.inArray = true; // Ensure we note that we are in an array
+
if (!(type_ & (1 << kArraySchemaType))) {
DisallowedType(context, GetArrayString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
- context.arrayElementIndex = 0;
- context.inArray = true;
-
return CreateParallelValidator(context);
}
bool EndArray(Context& context, SizeType elementCount) const {
context.inArray = false;
if (elementCount < minItems_) {
context.error_handler.TooFewItems(elementCount, minItems_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
}
if (elementCount > maxItems_) {
context.error_handler.TooManyItems(elementCount, maxItems_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
}
return true;
}
+ static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
+ switch (validateErrorCode) {
+ case kValidateErrorMultipleOf: return GetMultipleOfString();
+ case kValidateErrorMaximum: return GetMaximumString();
+ case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
+ case kValidateErrorMinimum: return GetMinimumString();
+ case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
+
+ case kValidateErrorMaxLength: return GetMaxLengthString();
+ case kValidateErrorMinLength: return GetMinLengthString();
+ case kValidateErrorPattern: return GetPatternString();
+
+ case kValidateErrorMaxItems: return GetMaxItemsString();
+ case kValidateErrorMinItems: return GetMinItemsString();
+ case kValidateErrorUniqueItems: return GetUniqueItemsString();
+ case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
+
+ case kValidateErrorMaxProperties: return GetMaxPropertiesString();
+ case kValidateErrorMinProperties: return GetMinPropertiesString();
+ case kValidateErrorRequired: return GetRequiredString();
+ case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
+ case kValidateErrorPatternProperties: return GetPatternPropertiesString();
+ case kValidateErrorDependencies: return GetDependenciesString();
+
+ case kValidateErrorEnum: return GetEnumString();
+ case kValidateErrorType: return GetTypeString();
+
+ case kValidateErrorOneOf: return GetOneOfString();
+ case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
+ case kValidateErrorAllOf: return GetAllOfString();
+ case kValidateErrorAnyOf: return GetAnyOfString();
+ case kValidateErrorNot: return GetNotString();
+
+ default: return GetNullString();
+ }
+ }
+
+
// Generate functions for string literal according to Ch
#define RAPIDJSON_STRING_(name, ...) \
static const ValueType& Get##name##String() {\
static const Ch s[] = { __VA_ARGS__, '\0' };\
static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
@@ -1188,35 +1266,36 @@
if (validatorCount_) {
RAPIDJSON_ASSERT(context.validators == 0);
context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
context.validatorCount = validatorCount_;
+ // Always return after first failure for these sub-validators
if (allOf_.schemas)
- CreateSchemaValidators(context, allOf_);
+ CreateSchemaValidators(context, allOf_, false);
if (anyOf_.schemas)
- CreateSchemaValidators(context, anyOf_);
+ CreateSchemaValidators(context, anyOf_, false);
if (oneOf_.schemas)
- CreateSchemaValidators(context, oneOf_);
+ CreateSchemaValidators(context, oneOf_, false);
if (not_)
- context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
-
+ context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
+
if (hasSchemaDependencies_) {
for (SizeType i = 0; i < propertyCount_; i++)
if (properties_[i].dependenciesSchema)
- context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
+ context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
}
}
return true;
}
- void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
+ void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
for (SizeType i = 0; i < schemas.count; i++)
- context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
+ context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
}
// O(n)
bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
SizeType len = name.GetStringLength();
@@ -1232,33 +1311,33 @@
}
bool CheckInt(Context& context, int64_t i) const {
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
DisallowedType(context, GetIntegerString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
if (!minimum_.IsNull()) {
if (minimum_.IsInt64()) {
if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
}
}
else if (minimum_.IsUint64()) {
context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
}
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
return false;
}
if (!maximum_.IsNull()) {
if (maximum_.IsInt64()) {
if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
}
}
else if (maximum_.IsUint64()) { }
/* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
@@ -1267,11 +1346,11 @@
if (!multipleOf_.IsNull()) {
if (multipleOf_.IsUint64()) {
if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
context.error_handler.NotMultipleOf(i, multipleOf_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
}
}
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
return false;
}
@@ -1280,18 +1359,18 @@
}
bool CheckUint(Context& context, uint64_t i) const {
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
DisallowedType(context, GetIntegerString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
}
if (!minimum_.IsNull()) {
if (minimum_.IsUint64()) {
if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
}
}
else if (minimum_.IsInt64())
/* do nothing */; // i >= 0 > minimum.Getint64()
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
@@ -1300,26 +1379,26 @@
if (!maximum_.IsNull()) {
if (maximum_.IsUint64()) {
if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
}
}
else if (maximum_.IsInt64()) {
context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
}
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
return false;
}
if (!multipleOf_.IsNull()) {
if (multipleOf_.IsUint64()) {
if (i % multipleOf_.GetUint64() != 0) {
context.error_handler.NotMultipleOf(i, multipleOf_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
}
}
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
return false;
}
@@ -1328,30 +1407,30 @@
}
bool CheckDoubleMinimum(Context& context, double d) const {
if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
}
return true;
}
bool CheckDoubleMaximum(Context& context, double d) const {
if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
}
return true;
}
bool CheckDoubleMultipleOf(Context& context, double d) const {
double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
double q = std::floor(a / b);
double r = a - q * b;
if (r > 0.0) {
context.error_handler.NotMultipleOf(d, multipleOf_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
}
return true;
}
void DisallowedType(Context& context, const ValueType& actualType) const {
@@ -1761,12 +1840,11 @@
typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
typename StateAllocator = CrtAllocator>
class GenericSchemaValidator :
public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
public internal::ISchemaValidator,
- public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType>
-{
+ public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
public:
typedef typename SchemaDocumentType::SchemaType SchemaType;
typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename SchemaType::EncodingType EncodingType;
typedef typename SchemaType::SValue SValue;
@@ -1795,11 +1873,12 @@
documentStack_(allocator, documentStackCapacity),
outputHandler_(0),
error_(kObjectType),
currentError_(),
missingDependents_(),
- valid_(true)
+ valid_(true),
+ flags_(kValidateDefaultFlags)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0)
#endif
{
}
@@ -1826,11 +1905,12 @@
documentStack_(allocator, documentStackCapacity),
outputHandler_(&outputHandler),
error_(kObjectType),
currentError_(),
missingDependents_(),
- valid_(true)
+ valid_(true),
+ flags_(kValidateDefaultFlags)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0)
#endif
{
}
@@ -1844,120 +1924,150 @@
//! Reset the internal states.
void Reset() {
while (!schemaStack_.Empty())
PopSchema();
documentStack_.Clear();
+ ResetError();
+ }
+
+ //! Reset the error state.
+ void ResetError() {
error_.SetObject();
currentError_.SetNull();
missingDependents_.SetNull();
valid_ = true;
}
+ //! Implementation of ISchemaValidator
+ void SetValidateFlags(unsigned flags) {
+ flags_ = flags;
+ }
+ virtual unsigned GetValidateFlags() const {
+ return flags_;
+ }
+
//! Checks whether the current state is valid.
// Implementation of ISchemaValidator
- virtual bool IsValid() const { return valid_; }
+ virtual bool IsValid() const {
+ if (!valid_) return false;
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
+ return true;
+ }
//! Gets the error object.
ValueType& GetError() { return error_; }
const ValueType& GetError() const { return error_; }
//! Gets the JSON pointer pointed to the invalid schema.
+ // If reporting all errors, the stack will be empty.
PointerType GetInvalidSchemaPointer() const {
return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
}
//! Gets the keyword of invalid schema.
+ // If reporting all errors, the stack will be empty, so return "errors".
const Ch* GetInvalidSchemaKeyword() const {
- return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
+ if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString();
+ return 0;
}
+ //! Gets the error code of invalid schema.
+ // If reporting all errors, the stack will be empty, so return kValidateErrors.
+ ValidateErrorCode GetInvalidSchemaCode() const {
+ if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
+ if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
+ return kValidateErrorNone;
+ }
+
//! Gets the JSON pointer pointed to the invalid value.
+ // If reporting all errors, the stack will be empty.
PointerType GetInvalidDocumentPointer() const {
if (documentStack_.Empty()) {
return PointerType();
}
else {
return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
}
}
void NotMultipleOf(int64_t actual, const SValue& expected) {
- AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
}
void NotMultipleOf(uint64_t actual, const SValue& expected) {
- AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
}
void NotMultipleOf(double actual, const SValue& expected) {
- AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
+ AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
}
void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
}
void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
}
void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
- AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
+ AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
}
void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
}
void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
}
void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
- AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
+ AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
}
void TooLong(const Ch* str, SizeType length, SizeType expected) {
- AddNumberError(SchemaType::GetMaxLengthString(),
+ AddNumberError(kValidateErrorMaxLength,
ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
}
void TooShort(const Ch* str, SizeType length, SizeType expected) {
- AddNumberError(SchemaType::GetMinLengthString(),
+ AddNumberError(kValidateErrorMinLength,
ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
}
void DoesNotMatch(const Ch* str, SizeType length) {
currentError_.SetObject();
currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
- AddCurrentError(SchemaType::GetPatternString());
+ AddCurrentError(kValidateErrorPattern);
}
void DisallowedItem(SizeType index) {
currentError_.SetObject();
currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
- AddCurrentError(SchemaType::GetAdditionalItemsString(), true);
+ AddCurrentError(kValidateErrorAdditionalItems, true);
}
void TooFewItems(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(SchemaType::GetMinItemsString(),
+ AddNumberError(kValidateErrorMinItems,
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void TooManyItems(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(SchemaType::GetMaxItemsString(),
+ AddNumberError(kValidateErrorMaxItems,
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void DuplicateItems(SizeType index1, SizeType index2) {
ValueType duplicates(kArrayType);
duplicates.PushBack(index1, GetStateAllocator());
duplicates.PushBack(index2, GetStateAllocator());
currentError_.SetObject();
currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
- AddCurrentError(SchemaType::GetUniqueItemsString(), true);
+ AddCurrentError(kValidateErrorUniqueItems, true);
}
void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(SchemaType::GetMaxPropertiesString(),
+ AddNumberError(kValidateErrorMaxProperties,
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(SchemaType::GetMinPropertiesString(),
+ AddNumberError(kValidateErrorMinProperties,
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void StartMissingProperties() {
currentError_.SetArray();
}
@@ -1968,21 +2078,21 @@
if (currentError_.Empty())
return false;
ValueType error(kObjectType);
error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
currentError_ = error;
- AddCurrentError(SchemaType::GetRequiredString());
+ AddCurrentError(kValidateErrorRequired);
return true;
}
void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
for (SizeType i = 0; i < count; ++i)
MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
}
void DisallowedProperty(const Ch* name, SizeType length) {
currentError_.SetObject();
currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
- AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true);
+ AddCurrentError(kValidateErrorAdditionalProperties, true);
}
void StartDependencyErrors() {
currentError_.SetObject();
}
@@ -1991,13 +2101,24 @@
}
void AddMissingDependentProperty(const SValue& targetName) {
missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
}
void EndMissingDependentProperties(const SValue& sourceName) {
- if (!missingDependents_.Empty())
- currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
- missingDependents_, GetStateAllocator());
+ if (!missingDependents_.Empty()) {
+ // Create equivalent 'required' error
+ ValueType error(kObjectType);
+ ValidateErrorCode code = kValidateErrorRequired;
+ error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
+ AddErrorCode(error, code);
+ AddErrorInstanceLocation(error, false);
+ // When appending to a pointer ensure its allocator is used
+ PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
+ AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
+ ValueType wrapper(kObjectType);
+ wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
+ currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
+ }
}
void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
}
@@ -2005,17 +2126,17 @@
if (currentError_.ObjectEmpty())
return false;
ValueType error(kObjectType);
error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
currentError_ = error;
- AddCurrentError(SchemaType::GetDependenciesString());
+ AddCurrentError(kValidateErrorDependencies);
return true;
}
- void DisallowedValue() {
+ void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
currentError_.SetObject();
- AddCurrentError(SchemaType::GetEnumString());
+ AddCurrentError(code);
}
void StartDisallowedType() {
currentError_.SetArray();
}
void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
@@ -2024,26 +2145,28 @@
void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
ValueType error(kObjectType);
error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
currentError_ = error;
- AddCurrentError(SchemaType::GetTypeString());
+ AddCurrentError(kValidateErrorType);
}
void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
- for (SizeType i = 0; i < count; ++i) {
- MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
- }
+ // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
+ AddErrorArray(kValidateErrorAllOf, subvalidators, count);
+ //for (SizeType i = 0; i < count; ++i) {
+ // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
+ //}
}
void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
- AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count);
+ AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
}
- void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
- AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count);
+ void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) {
+ AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count);
}
void Disallowed() {
currentError_.SetObject();
- AddCurrentError(SchemaType::GetNotString());
+ AddCurrentError(kValidateErrorNot);
}
#define RAPIDJSON_STRING_(name, ...) \
static const StringRefType& Get##name##String() {\
static const Ch s[] = { __VA_ARGS__, '\0' };\
@@ -2056,10 +2179,12 @@
RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
+ RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
+ RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
#undef RAPIDJSON_STRING_
#if RAPIDJSON_SCHEMA_VERBOSE
@@ -2073,11 +2198,11 @@
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
#endif
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
if (!valid_) return false; \
- if (!BeginValue() || !CurrentSchema().method arg1) {\
+ if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
return valid_ = false;\
}
#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
@@ -2091,11 +2216,12 @@
for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
}
#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
- return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
+ valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
+ return valid_;
#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
@@ -2119,19 +2245,19 @@
}
bool Key(const Ch* str, SizeType len, bool copy) {
if (!valid_) return false;
AppendToken(str, len);
- if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
+ if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
}
- bool EndObject(SizeType memberCount) {
+ bool EndObject(SizeType memberCount) {
if (!valid_) return false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
- if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
+ if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
}
bool StartArray() {
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
@@ -2140,26 +2266,28 @@
}
bool EndArray(SizeType elementCount) {
if (!valid_) return false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
- if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
+ if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
}
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
// Implementation of ISchemaStateFactory<SchemaType>
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
- return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
+ ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
#if RAPIDJSON_SCHEMA_VERBOSE
depth_ + 1,
#endif
&GetStateAllocator());
+ sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag);
+ return sv;
}
virtual void DestroySchemaValidator(ISchemaValidator* validator) {
GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
v->~GenericSchemaValidator();
@@ -2212,11 +2340,12 @@
documentStack_(allocator, documentStackCapacity),
outputHandler_(0),
error_(kObjectType),
currentError_(),
missingDependents_(),
- valid_(true)
+ valid_(true),
+ flags_(kValidateDefaultFlags)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(depth)
#endif
{
if (basePath && basePathSize)
@@ -2227,18 +2356,22 @@
if (!stateAllocator_)
stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
return *stateAllocator_;
}
+ bool GetContinueOnErrors() const {
+ return flags_ & kValidateContinueOnErrorFlag;
+ }
+
bool BeginValue() {
if (schemaStack_.Empty())
PushSchema(root_);
else {
if (CurrentContext().inArray)
internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
- if (!CurrentSchema().BeginValue(CurrentContext()))
+ if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
return false;
SizeType count = CurrentContext().patternPropertiesSchemaCount;
const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
@@ -2250,45 +2383,51 @@
CurrentContext().objectPatternValidatorType = patternValidatorType;
ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
for (SizeType i = 0; i < count; i++)
- va[validatorCount++] = CreateSchemaValidator(*sa[i]);
+ va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
}
CurrentContext().arrayUniqueness = valueUniqueness;
}
return true;
}
bool EndValue() {
- if (!CurrentSchema().EndValue(CurrentContext()))
+ if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
return false;
#if RAPIDJSON_SCHEMA_VERBOSE
GenericStringBuffer<EncodingType> sb;
schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
*documentStack_.template Push<Ch>() = '\0';
documentStack_.template Pop<Ch>(1);
internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
#endif
-
- uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
+ void* hasher = CurrentContext().hasher;
+ uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
PopSchema();
if (!schemaStack_.Empty()) {
Context& context = CurrentContext();
- if (context.valueUniqueness) {
+ // Only check uniqueness if there is a hasher
+ if (hasher && context.valueUniqueness) {
HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
if (!a)
CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
if (itr->GetUint64() == h) {
DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
- RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
+ // Cleanup before returning if continuing
+ if (GetContinueOnErrors()) {
+ a->PushBack(h, GetStateAllocator());
+ while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
+ }
+ RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
}
a->PushBack(h, GetStateAllocator());
}
}
@@ -2325,29 +2464,36 @@
StateAllocator::Free(a);
}
c->~Context();
}
- void AddErrorLocation(ValueType& result, bool parent) {
+ void AddErrorInstanceLocation(ValueType& result, bool parent) {
GenericStringBuffer<EncodingType> sb;
PointerType instancePointer = GetInvalidDocumentPointer();
((parent && instancePointer.GetTokenCount() > 0)
- ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
- : instancePointer).StringifyUriFragment(sb);
+ ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
+ : instancePointer).StringifyUriFragment(sb);
ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
- GetStateAllocator());
+ GetStateAllocator());
result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
- sb.Clear();
- memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()),
- CurrentSchema().GetURI().GetString(),
- CurrentSchema().GetURI().GetStringLength() * sizeof(Ch));
- GetInvalidSchemaPointer().StringifyUriFragment(sb);
+ }
+
+ void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
+ GenericStringBuffer<EncodingType> sb;
+ SizeType len = CurrentSchema().GetURI().GetStringLength();
+ if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
+ if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
+ else GetInvalidSchemaPointer().StringifyUriFragment(sb);
ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
GetStateAllocator());
result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
}
+ void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
+ result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
+ }
+
void AddError(ValueType& keyword, ValueType& error) {
typename ValueType::MemberIterator member = error_.FindMember(keyword);
if (member == error_.MemberEnd())
error_.AddMember(keyword, error, GetStateAllocator());
else {
@@ -2358,39 +2504,41 @@
}
member->value.PushBack(error, GetStateAllocator());
}
}
- void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) {
- AddErrorLocation(currentError_, parent);
- AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_);
+ void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
+ AddErrorCode(currentError_, code);
+ AddErrorInstanceLocation(currentError_, parent);
+ AddErrorSchemaLocation(currentError_);
+ AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
}
void MergeError(ValueType& other) {
for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
AddError(it->name, it->value);
}
}
- void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected,
+ void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
const typename SchemaType::ValueType& (*exclusive)() = 0) {
currentError_.SetObject();
currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
if (exclusive)
currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
- AddCurrentError(keyword);
+ AddCurrentError(code);
}
- void AddErrorArray(const typename SchemaType::ValueType& keyword,
+ void AddErrorArray(const ValidateErrorCode code,
ISchemaValidator** subvalidators, SizeType count) {
ValueType errors(kArrayType);
for (SizeType i = 0; i < count; ++i)
errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
currentError_.SetObject();
currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
- AddCurrentError(keyword);
+ AddCurrentError(code);
}
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
@@ -2406,10 +2554,11 @@
OutputHandler* outputHandler_;
ValueType error_;
ValueType currentError_;
ValueType missingDependents_;
bool valid_;
+ unsigned flags_;
#if RAPIDJSON_SCHEMA_VERBOSE
unsigned depth_;
#endif
};
@@ -2443,11 +2592,11 @@
//! Constructor
/*!
\param is Input stream.
\param sd Schema document.
*/
- SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {}
+ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
template <typename Handler>
bool operator()(Handler& handler) {
GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
@@ -2461,10 +2610,11 @@
error_.SetObject();
}
else {
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
+ invalidSchemaCode_ = validator.GetInvalidSchemaCode();
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
error_.CopyFrom(validator.GetError(), allocator_);
}
return parseResult_;
@@ -2474,18 +2624,20 @@
bool IsValid() const { return isValid_; }
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
const ValueType& GetError() const { return error_; }
+ ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
private:
InputStream& is_;
const SchemaDocumentType& sd_;
ParseResult parseResult_;
PointerType invalidSchemaPointer_;
const Ch* invalidSchemaKeyword_;
PointerType invalidDocumentPointer_;
+ ValidateErrorCode invalidSchemaCode_;
StackAllocator allocator_;
ValueType error_;
bool isValid_;
};