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_; };