Commit f716c3bf authored by Yuri Khan's avatar Yuri Khan

Report schema violation details (#619)

parent c2371584
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "document.h" #include "document.h"
#include "pointer.h" #include "pointer.h"
#include "stringbuffer.h"
#include <cmath> // abs, floor #include <cmath> // abs, floor
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
...@@ -157,6 +158,62 @@ public: ...@@ -157,6 +158,62 @@ public:
virtual void FreeState(void* p) = 0; virtual void FreeState(void* p) = 0;
}; };
///////////////////////////////////////////////////////////////////////////////
// IValidationErrorHandler
template <typename SchemaType>
class IValidationErrorHandler {
public:
typedef typename SchemaType::Ch Ch;
typedef typename SchemaType::SValue SValue;
virtual ~IValidationErrorHandler() {}
virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
virtual void DisallowedItem(SizeType index) = 0;
virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
virtual void StartMissingProperties() = 0;
virtual void AddMissingProperty(const SValue& name) = 0;
virtual bool EndMissingProperties() = 0;
virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
virtual void StartDependencyErrors() = 0;
virtual void StartMissingDependentProperties() = 0;
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 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 Disallowed() = 0;
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Hasher // Hasher
...@@ -345,6 +402,7 @@ public: ...@@ -345,6 +402,7 @@ public:
typedef SchemaValidationContext<SchemaDocumentType> Context; typedef SchemaValidationContext<SchemaDocumentType> Context;
typedef Schema<SchemaDocumentType> SchemaType; typedef Schema<SchemaDocumentType> SchemaType;
typedef GenericValue<EncodingType, AllocatorType> SValue; typedef GenericValue<EncodingType, AllocatorType> SValue;
typedef IValidationErrorHandler<Schema> ErrorHandler;
friend class GenericSchemaDocument<ValueType, AllocatorType>; friend class GenericSchemaDocument<ValueType, AllocatorType>;
Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
...@@ -606,7 +664,7 @@ public: ...@@ -606,7 +664,7 @@ public:
return pointer_; return pointer_;
} }
bool BeginValue(Context& context) const { bool BeginValue(Context& context, ErrorHandler& eh) const {
if (context.inArray) { if (context.inArray) {
if (uniqueItems_) if (uniqueItems_)
context.valueUniqueness = true; context.valueUniqueness = true;
...@@ -620,9 +678,11 @@ public: ...@@ -620,9 +678,11 @@ public:
context.valueSchema = additionalItemsSchema_; context.valueSchema = additionalItemsSchema_;
else if (additionalItems_) else if (additionalItems_)
context.valueSchema = typeless_; context.valueSchema = typeless_;
else else {
eh.DisallowedItem(context.arrayElementIndex);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
} }
}
else else
context.valueSchema = typeless_; context.valueSchema = typeless_;
...@@ -631,7 +691,7 @@ public: ...@@ -631,7 +691,7 @@ public:
return true; return true;
} }
RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { RAPIDJSON_FORCEINLINE bool EndValue(Context& context, ErrorHandler& eh) const {
if (context.patternPropertiesValidatorCount > 0) { if (context.patternPropertiesValidatorCount > 0) {
bool otherValid = false; bool otherValid = false;
SizeType count = context.patternPropertiesValidatorCount; SizeType count = context.patternPropertiesValidatorCount;
...@@ -646,35 +706,45 @@ public: ...@@ -646,35 +706,45 @@ public:
} }
if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
if (!patternValid) if (!patternValid) {
eh.PropertyViolations(context.patternPropertiesValidators, count);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
} }
}
else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
if (!patternValid || !otherValid) if (!patternValid || !otherValid) {
eh.PropertyViolations(context.patternPropertiesValidators, count + 1);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
} }
else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) }
else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
eh.PropertyViolations(context.patternPropertiesValidators, count + 1);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
} }
}
if (enum_) { if (enum_) {
const uint64_t h = context.factory.GetHashCode(context.hasher); const uint64_t h = context.factory.GetHashCode(context.hasher);
for (SizeType i = 0; i < enumCount_; i++) for (SizeType i = 0; i < enumCount_; i++)
if (enum_[i] == h) if (enum_[i] == h)
goto foundEnum; goto foundEnum;
eh.DisallowedValue();
RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
foundEnum:; foundEnum:;
} }
if (allOf_.schemas) if (allOf_.schemas)
for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
if (!context.validators[i]->IsValid()) if (!context.validators[i]->IsValid()) {
eh.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
}
if (anyOf_.schemas) { if (anyOf_.schemas) {
for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
if (context.validators[i]->IsValid()) if (context.validators[i]->IsValid())
goto foundAny; goto foundAny;
eh.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
foundAny:; foundAny:;
} }
...@@ -683,96 +753,117 @@ public: ...@@ -683,96 +753,117 @@ public:
bool oneValid = false; bool oneValid = false;
for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
if (context.validators[i]->IsValid()) { if (context.validators[i]->IsValid()) {
if (oneValid) if (oneValid) {
eh.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
else } else
oneValid = true; oneValid = true;
} }
if (!oneValid) if (!oneValid) {
eh.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
} }
}
if (not_ && context.validators[notValidatorIndex_]->IsValid()) if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
eh.Disallowed();
RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
}
return true; return true;
} }
bool Null(Context& context) const { bool Null(Context& context, ErrorHandler& eh) const {
if (!(type_ & (1 << kNullSchemaType))) if (!(type_ & (1 << kNullSchemaType))) {
DisallowedType(eh, GetNullString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Bool(Context& context, bool) const { bool Bool(Context& context, ErrorHandler& eh, bool) const {
if (!(type_ & (1 << kBooleanSchemaType))) if (!(type_ & (1 << kBooleanSchemaType))) {
DisallowedType(eh, GetBooleanString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Int(Context& context, int i) const { bool Int(Context& context, ErrorHandler& eh, int i) const {
if (!CheckInt(context, i)) if (!CheckInt(context, eh, i))
return false; return false;
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Uint(Context& context, unsigned u) const { bool Uint(Context& context, ErrorHandler& eh, unsigned u) const {
if (!CheckUint(context, u)) if (!CheckUint(context, eh, u))
return false; return false;
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Int64(Context& context, int64_t i) const { bool Int64(Context& context, ErrorHandler& eh, int64_t i) const {
if (!CheckInt(context, i)) if (!CheckInt(context, eh, i))
return false; return false;
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Uint64(Context& context, uint64_t u) const { bool Uint64(Context& context, ErrorHandler& eh, uint64_t u) const {
if (!CheckUint(context, u)) if (!CheckUint(context, eh, u))
return false; return false;
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Double(Context& context, double d) const { bool Double(Context& context, ErrorHandler& eh, double d) const {
if (!(type_ & (1 << kNumberSchemaType))) if (!(type_ & (1 << kNumberSchemaType))) {
DisallowedType(eh, GetNumberString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) if (!minimum_.IsNull() && !CheckDoubleMinimum(context, eh, d))
return false; return false;
if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) if (!maximum_.IsNull() && !CheckDoubleMaximum(context, eh, d))
return false; return false;
if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, eh, d))
return false; return false;
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool String(Context& context, const Ch* str, SizeType length, bool) const { bool String(Context& context, ErrorHandler& eh, const Ch* str, SizeType length, bool) const {
if (!(type_ & (1 << kStringSchemaType))) if (!(type_ & (1 << kStringSchemaType))) {
DisallowedType(eh, GetStringString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
if (minLength_ != 0 || maxLength_ != SizeType(~0)) { if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
SizeType count; SizeType count;
if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) { if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
if (count < minLength_) if (count < minLength_) {
eh.TooShort(str, length, minLength_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
if (count > maxLength_) }
if (count > maxLength_) {
eh.TooLong(str, length, maxLength_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
} }
} }
}
if (pattern_ && !IsPatternMatch(pattern_, str, length)) if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
eh.DoesNotMatch(str, length);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
}
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool StartObject(Context& context) const { bool StartObject(Context& context, ErrorHandler& eh) const {
if (!(type_ & (1 << kObjectSchemaType))) if (!(type_ & (1 << kObjectSchemaType))) {
DisallowedType(eh, GetObjectString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
if (hasDependencies_ || hasRequired_) { if (hasDependencies_ || hasRequired_) {
context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_)); context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
...@@ -789,7 +880,7 @@ public: ...@@ -789,7 +880,7 @@ public:
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool Key(Context& context, const Ch* str, SizeType len, bool) const { bool Key(Context& context, ErrorHandler& eh, const Ch* str, SizeType len, bool) const {
if (patternProperties_) { if (patternProperties_) {
context.patternPropertiesSchemaCount = 0; context.patternPropertiesSchemaCount = 0;
for (SizeType i = 0; i < patternPropertyCount_; i++) for (SizeType i = 0; i < patternPropertyCount_; i++)
...@@ -830,45 +921,65 @@ public: ...@@ -830,45 +921,65 @@ public:
return true; return true;
} }
if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
eh.DisallowedProperty(str, len);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
}
return true; return true;
} }
bool EndObject(Context& context, SizeType memberCount) const { bool EndObject(Context& context, ErrorHandler& eh, SizeType memberCount) const {
if (hasRequired_) if (hasRequired_) {
eh.StartMissingProperties();
for (SizeType index = 0; index < propertyCount_; index++) for (SizeType index = 0; index < propertyCount_; index++)
if (properties_[index].required) if (properties_[index].required && !context.propertyExist[index])
if (!context.propertyExist[index]) eh.AddMissingProperty(properties_[index].name);
if (eh.EndMissingProperties())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
}
if (memberCount < minProperties_) if (memberCount < minProperties_) {
eh.TooFewProperties(memberCount, minProperties_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
}
if (memberCount > maxProperties_) if (memberCount > maxProperties_) {
eh.TooManyProperties(memberCount, maxProperties_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
}
if (hasDependencies_) { if (hasDependencies_) {
for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) eh.StartDependencyErrors();
for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
const Property& source = properties_[sourceIndex];
if (context.propertyExist[sourceIndex]) { if (context.propertyExist[sourceIndex]) {
if (properties_[sourceIndex].dependencies) { if (source.dependencies) {
eh.StartMissingDependentProperties();
for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); eh.AddMissingDependentProperty(properties_[targetIndex].name);
eh.EndMissingDependentProperties(source.name);
} }
else if (properties_[sourceIndex].dependenciesSchema) else if (source.dependenciesSchema) {
if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); if (!dependenciesValidator->IsValid())
eh.AddDependencySchemaError(source.name, dependenciesValidator);
} }
} }
}
if (eh.EndDependencyErrors())
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
}
return true; return true;
} }
bool StartArray(Context& context) const { bool StartArray(Context& context, ErrorHandler& eh) const {
if (!(type_ & (1 << kArraySchemaType))) if (!(type_ & (1 << kArraySchemaType))) {
DisallowedType(eh, GetArrayString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
context.arrayElementIndex = 0; context.arrayElementIndex = 0;
context.inArray = true; context.inArray = true;
...@@ -876,14 +987,18 @@ public: ...@@ -876,14 +987,18 @@ public:
return CreateParallelValidator(context); return CreateParallelValidator(context);
} }
bool EndArray(Context& context, SizeType elementCount) const { bool EndArray(Context& context, ErrorHandler& eh, SizeType elementCount) const {
context.inArray = false; context.inArray = false;
if (elementCount < minItems_) if (elementCount < minItems_) {
eh.TooFewItems(elementCount, minItems_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
}
if (elementCount > maxItems_) if (elementCount > maxItems_) {
eh.TooManyItems(elementCount, maxItems_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
}
return true; return true;
} }
...@@ -1103,104 +1218,144 @@ private: ...@@ -1103,104 +1218,144 @@ private:
return false; return false;
} }
bool CheckInt(Context& context, int64_t i) const { bool CheckInt(Context& context, ErrorHandler& eh, int64_t i) const {
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
DisallowedType(eh, GetIntegerString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
if (!minimum_.IsNull()) { if (!minimum_.IsNull()) {
if (minimum_.IsInt64()) { if (minimum_.IsInt64()) {
if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
eh.BelowMinimum(i, minimum_, exclusiveMinimum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
} }
}
else if (minimum_.IsUint64()) { else if (minimum_.IsUint64()) {
eh.BelowMinimum(i, minimum_, exclusiveMinimum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
} }
else if (!CheckDoubleMinimum(context, static_cast<double>(i))) else if (!CheckDoubleMinimum(context, eh, static_cast<double>(i)))
return false; return false;
} }
if (!maximum_.IsNull()) { if (!maximum_.IsNull()) {
if (maximum_.IsInt64()) { if (maximum_.IsInt64()) {
if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
eh.AboveMaximum(i, maximum_, exclusiveMaximum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
} }
}
else if (maximum_.IsUint64()) { } else if (maximum_.IsUint64()) { }
/* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
else if (!CheckDoubleMaximum(context, static_cast<double>(i))) else if (!CheckDoubleMaximum(context, eh, static_cast<double>(i)))
return false; return false;
} }
if (!multipleOf_.IsNull()) { if (!multipleOf_.IsNull()) {
if (multipleOf_.IsUint64()) { if (multipleOf_.IsUint64()) {
if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
eh.NotMultipleOf(i, multipleOf_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
} }
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i))) }
else if (!CheckDoubleMultipleOf(context, eh, static_cast<double>(i)))
return false; return false;
} }
return true; return true;
} }
bool CheckUint(Context& context, uint64_t i) const { bool CheckUint(Context& context, ErrorHandler& eh, uint64_t i) const {
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
DisallowedType(eh, GetIntegerString());
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
}
if (!minimum_.IsNull()) { if (!minimum_.IsNull()) {
if (minimum_.IsUint64()) { if (minimum_.IsUint64()) {
if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
eh.BelowMinimum(i, minimum_, exclusiveMinimum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
} }
}
else if (minimum_.IsInt64()) else if (minimum_.IsInt64())
/* do nothing */; // i >= 0 > minimum.Getint64() /* do nothing */; // i >= 0 > minimum.Getint64()
else if (!CheckDoubleMinimum(context, static_cast<double>(i))) else if (!CheckDoubleMinimum(context, eh, static_cast<double>(i)))
return false; return false;
} }
if (!maximum_.IsNull()) { if (!maximum_.IsNull()) {
if (maximum_.IsUint64()) { if (maximum_.IsUint64()) {
if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
eh.AboveMaximum(i, maximum_, exclusiveMaximum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
} }
else if (maximum_.IsInt64()) }
else if (maximum_.IsInt64()) {
eh.AboveMaximum(i, maximum_, exclusiveMaximum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
else if (!CheckDoubleMaximum(context, static_cast<double>(i))) }
else if (!CheckDoubleMaximum(context, eh, static_cast<double>(i)))
return false; return false;
} }
if (!multipleOf_.IsNull()) { if (!multipleOf_.IsNull()) {
if (multipleOf_.IsUint64()) { if (multipleOf_.IsUint64()) {
if (i % multipleOf_.GetUint64() != 0) if (i % multipleOf_.GetUint64() != 0) {
eh.NotMultipleOf(i, multipleOf_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
} }
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i))) }
else if (!CheckDoubleMultipleOf(context, eh, static_cast<double>(i)))
return false; return false;
} }
return true; return true;
} }
bool CheckDoubleMinimum(Context& context, double d) const { bool CheckDoubleMinimum(Context& context, ErrorHandler& eh, double d) const {
if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
eh.BelowMinimum(d, minimum_, exclusiveMinimum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
}
return true; return true;
} }
bool CheckDoubleMaximum(Context& context, double d) const { bool CheckDoubleMaximum(Context& context, ErrorHandler& eh, double d) const {
if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
eh.AboveMaximum(d, maximum_, exclusiveMaximum_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
}
return true; return true;
} }
bool CheckDoubleMultipleOf(Context& context, double d) const { bool CheckDoubleMultipleOf(Context& context, ErrorHandler& eh, double d) const {
double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
double q = std::floor(a / b); double q = std::floor(a / b);
double r = a - q * b; double r = a - q * b;
if (r > 0.0) if (r > 0.0) {
eh.NotMultipleOf(d, multipleOf_);
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
}
return true; return true;
} }
void DisallowedType(ErrorHandler& eh, const ValueType& actualType) const {
eh.StartDisallowedType();
if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
eh.EndDisallowedType(actualType);
}
struct Property { struct Property {
Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
~Property() { AllocatorType::Free(dependencies); } ~Property() { AllocatorType::Free(dependencies); }
...@@ -1590,13 +1745,17 @@ template < ...@@ -1590,13 +1745,17 @@ template <
typename StateAllocator = CrtAllocator> typename StateAllocator = CrtAllocator>
class GenericSchemaValidator : class GenericSchemaValidator :
public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>, public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
public internal::ISchemaValidator public internal::ISchemaValidator,
public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType>
{ {
public: public:
typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::SchemaType SchemaType;
typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename SchemaType::EncodingType EncodingType; typedef typename SchemaType::EncodingType EncodingType;
typedef typename SchemaType::SValue SValue;
typedef typename EncodingType::Ch Ch; typedef typename EncodingType::Ch Ch;
typedef GenericStringRef<Ch> StringRefType;
typedef GenericValue<EncodingType, StateAllocator> ValueType;
//! Constructor without output handler. //! Constructor without output handler.
/*! /*!
...@@ -1618,6 +1777,9 @@ public: ...@@ -1618,6 +1777,9 @@ public:
schemaStack_(allocator, schemaStackCapacity), schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity), documentStack_(allocator, documentStackCapacity),
outputHandler_(0), outputHandler_(0),
error_(kObjectType),
currentError_(),
missingDependents_(),
valid_(true) valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0) , depth_(0)
...@@ -1646,6 +1808,9 @@ public: ...@@ -1646,6 +1808,9 @@ public:
schemaStack_(allocator, schemaStackCapacity), schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity), documentStack_(allocator, documentStackCapacity),
outputHandler_(&outputHandler), outputHandler_(&outputHandler),
error_(kObjectType),
currentError_(),
missingDependents_(),
valid_(true) valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0) , depth_(0)
...@@ -1664,6 +1829,9 @@ public: ...@@ -1664,6 +1829,9 @@ public:
while (!schemaStack_.Empty()) while (!schemaStack_.Empty())
PopSchema(); PopSchema();
documentStack_.Clear(); documentStack_.Clear();
error_.SetObject();
currentError_.SetNull();
missingDependents_.SetNull();
valid_ = true; valid_ = true;
} }
...@@ -1671,6 +1839,10 @@ public: ...@@ -1671,6 +1839,10 @@ public:
// Implementation of ISchemaValidator // Implementation of ISchemaValidator
virtual bool IsValid() const { return valid_; } virtual bool IsValid() const { return valid_; }
//! Gets the error object.
ValueType& GetError() { return error_; }
const ValueType& GetError() const { return error_; }
//! Gets the JSON pointer pointed to the invalid schema. //! Gets the JSON pointer pointed to the invalid schema.
PointerType GetInvalidSchemaPointer() const { PointerType GetInvalidSchemaPointer() const {
return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
...@@ -1686,6 +1858,186 @@ public: ...@@ -1686,6 +1858,186 @@ public:
return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch)); return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
} }
void NotMultipleOf(int64_t actual, const SValue& expected) {
AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
}
void NotMultipleOf(uint64_t actual, const SValue& expected) {
AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
}
void NotMultipleOf(double actual, const SValue& expected) {
AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
}
void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
AddNumberError(SchemaType::GetMaximumString(), 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,
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
}
void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
AddNumberError(SchemaType::GetMaximumString(), 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,
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
}
void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
}
void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
}
void TooLong(const Ch* str, SizeType length, SizeType expected) {
AddNumberError(SchemaType::GetMaxLengthString(),
ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
}
void TooShort(const Ch* str, SizeType length, SizeType expected) {
AddNumberError(SchemaType::GetMinLengthString(),
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());
}
void DisallowedItem(SizeType index) {
currentError_.SetObject();
currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
AddCurrentError(SchemaType::GetAdditionalItemsString(), true);
}
void TooFewItems(SizeType actualCount, SizeType expectedCount) {
AddNumberError(SchemaType::GetMinItemsString(),
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void TooManyItems(SizeType actualCount, SizeType expectedCount) {
AddNumberError(SchemaType::GetMaxItemsString(),
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);
}
void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
AddNumberError(SchemaType::GetMaxPropertiesString(),
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
AddNumberError(SchemaType::GetMinPropertiesString(),
ValueType(actualCount).Move(), SValue(expectedCount).Move());
}
void StartMissingProperties() {
currentError_.SetArray();
}
void AddMissingProperty(const SValue& name) {
currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
}
bool EndMissingProperties() {
if (currentError_.Empty())
return false;
ValueType error(kObjectType);
error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
currentError_ = error;
AddCurrentError(SchemaType::GetRequiredString());
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);
}
void StartDependencyErrors() {
currentError_.SetObject();
}
void StartMissingDependentProperties() {
missingDependents_.SetArray();
}
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());
}
void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
}
bool EndDependencyErrors() {
if (currentError_.ObjectEmpty())
return false;
ValueType error(kObjectType);
error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
currentError_ = error;
AddCurrentError(SchemaType::GetDependenciesString());
return true;
}
void DisallowedValue() {
currentError_.SetObject();
AddCurrentError(SchemaType::GetEnumString());
}
void StartDisallowedType() {
currentError_.SetArray();
}
void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
}
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());
}
void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
AddErrorArray(SchemaType::GetAllOfString(), subvalidators, count);
}
void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count);
}
void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count);
}
void Disallowed() {
currentError_.SetObject();
AddCurrentError(SchemaType::GetNotString());
}
#define RAPIDJSON_STRING_(name, ...) \
static const StringRefType& Get##name##String() {\
static const Ch s[] = { __VA_ARGS__, '\0' };\
static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
return v;\
}
RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
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_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
#undef RAPIDJSON_STRING_
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
RAPIDJSON_MULTILINEMACRO_BEGIN\ RAPIDJSON_MULTILINEMACRO_BEGIN\
...@@ -1724,20 +2076,20 @@ RAPIDJSON_MULTILINEMACRO_END ...@@ -1724,20 +2076,20 @@ RAPIDJSON_MULTILINEMACRO_END
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext(), *this), ( )); }
bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), *this, b), (b)); }
bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), *this, i), (i)); }
bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), *this, u), (u)); }
bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), *this, i), (i)); }
bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), *this, u), (u)); }
bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), *this, d), (d)); }
bool RawNumber(const Ch* str, SizeType length, bool copy) bool RawNumber(const Ch* str, SizeType length, bool copy)
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), *this, str, length, copy), (str, length, copy)); }
bool String(const Ch* str, SizeType length, bool copy) bool String(const Ch* str, SizeType length, bool copy)
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), *this, str, length, copy), (str, length, copy)); }
bool StartObject() { bool StartObject() {
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext(), *this));
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
return valid_ = !outputHandler_ || outputHandler_->StartObject(); return valid_ = !outputHandler_ || outputHandler_->StartObject();
} }
...@@ -1745,7 +2097,7 @@ RAPIDJSON_MULTILINEMACRO_END ...@@ -1745,7 +2097,7 @@ RAPIDJSON_MULTILINEMACRO_END
bool Key(const Ch* str, SizeType len, bool copy) { bool Key(const Ch* str, SizeType len, bool copy) {
if (!valid_) return false; if (!valid_) return false;
AppendToken(str, len); AppendToken(str, len);
if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; if (!CurrentSchema().Key(CurrentContext(), *this, str, len, copy)) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
} }
...@@ -1753,12 +2105,12 @@ RAPIDJSON_MULTILINEMACRO_END ...@@ -1753,12 +2105,12 @@ RAPIDJSON_MULTILINEMACRO_END
bool EndObject(SizeType memberCount) { bool EndObject(SizeType memberCount) {
if (!valid_) return false; if (!valid_) return false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; if (!CurrentSchema().EndObject(CurrentContext(), *this, memberCount)) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
} }
bool StartArray() { bool StartArray() {
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext(), *this));
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
return valid_ = !outputHandler_ || outputHandler_->StartArray(); return valid_ = !outputHandler_ || outputHandler_->StartArray();
} }
...@@ -1766,7 +2118,7 @@ RAPIDJSON_MULTILINEMACRO_END ...@@ -1766,7 +2118,7 @@ RAPIDJSON_MULTILINEMACRO_END
bool EndArray(SizeType elementCount) { bool EndArray(SizeType elementCount) {
if (!valid_) return false; if (!valid_) return false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; if (!CurrentSchema().EndArray(CurrentContext(), *this, elementCount)) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
} }
...@@ -1777,7 +2129,7 @@ RAPIDJSON_MULTILINEMACRO_END ...@@ -1777,7 +2129,7 @@ RAPIDJSON_MULTILINEMACRO_END
// Implementation of ISchemaStateFactory<SchemaType> // Implementation of ISchemaStateFactory<SchemaType>
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
depth_ + 1, depth_ + 1,
#endif #endif
...@@ -1820,6 +2172,7 @@ private: ...@@ -1820,6 +2172,7 @@ private:
GenericSchemaValidator( GenericSchemaValidator(
const SchemaDocumentType& schemaDocument, const SchemaDocumentType& schemaDocument,
const SchemaType& root, const SchemaType& root,
const char* basePath, size_t basePathSize,
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
unsigned depth, unsigned depth,
#endif #endif
...@@ -1834,11 +2187,16 @@ private: ...@@ -1834,11 +2187,16 @@ private:
schemaStack_(allocator, schemaStackCapacity), schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity), documentStack_(allocator, documentStackCapacity),
outputHandler_(0), outputHandler_(0),
error_(kObjectType),
currentError_(),
missingDependents_(),
valid_(true) valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
, depth_(depth) , depth_(depth)
#endif #endif
{ {
if (basePath && basePathSize)
memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
} }
StateAllocator& GetStateAllocator() { StateAllocator& GetStateAllocator() {
...@@ -1854,7 +2212,7 @@ private: ...@@ -1854,7 +2212,7 @@ private:
if (CurrentContext().inArray) if (CurrentContext().inArray)
internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
if (!CurrentSchema().BeginValue(CurrentContext())) if (!CurrentSchema().BeginValue(CurrentContext(), *this))
return false; return false;
SizeType count = CurrentContext().patternPropertiesSchemaCount; SizeType count = CurrentContext().patternPropertiesSchemaCount;
...@@ -1879,7 +2237,7 @@ private: ...@@ -1879,7 +2237,7 @@ private:
} }
bool EndValue() { bool EndValue() {
if (!CurrentSchema().EndValue(CurrentContext())) if (!CurrentSchema().EndValue(CurrentContext(), *this))
return false; return false;
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
...@@ -1902,8 +2260,10 @@ private: ...@@ -1902,8 +2260,10 @@ private:
if (!a) if (!a)
CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
if (itr->GetUint64() == h) if (itr->GetUint64() == h) {
DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
}
a->PushBack(h, GetStateAllocator()); a->PushBack(h, GetStateAllocator());
} }
} }
...@@ -1943,6 +2303,70 @@ private: ...@@ -1943,6 +2303,70 @@ private:
c->~Context(); c->~Context();
} }
void AddErrorLocation(ValueType& result, bool parent) {
GenericStringBuffer<EncodingType> sb;
PointerType instancePointer = GetInvalidDocumentPointer();
((parent && instancePointer.GetTokenCount() > 0)
? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
: instancePointer).StringifyUriFragment(sb);
ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
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);
ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
GetStateAllocator());
result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
}
void AddError(ValueType& keyword, ValueType& error) {
typename ValueType::MemberIterator member = error_.FindMember(keyword);
if (member == error_.MemberEnd())
error_.AddMember(keyword, error, GetStateAllocator());
else {
if (member->value.IsObject()) {
ValueType errors(kArrayType);
errors.PushBack(member->value, GetStateAllocator());
member->value = errors;
}
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 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,
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);
}
void AddErrorArray(const typename SchemaType::ValueType& keyword,
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);
}
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; } const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); } Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); } const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
...@@ -1956,6 +2380,9 @@ private: ...@@ -1956,6 +2380,9 @@ private:
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch) internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
OutputHandler* outputHandler_; OutputHandler* outputHandler_;
ValueType error_;
ValueType currentError_;
ValueType missingDependents_;
bool valid_; bool valid_;
#if RAPIDJSON_SCHEMA_VERBOSE #if RAPIDJSON_SCHEMA_VERBOSE
unsigned depth_; unsigned depth_;
...@@ -1987,13 +2414,14 @@ class SchemaValidatingReader { ...@@ -1987,13 +2414,14 @@ class SchemaValidatingReader {
public: public:
typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename InputStream::Ch Ch; typedef typename InputStream::Ch Ch;
typedef GenericValue<SourceEncoding, StackAllocator> ValueType;
//! Constructor //! Constructor
/*! /*!
\param is Input stream. \param is Input stream.
\param sd Schema document. \param sd Schema document.
*/ */
SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {}
template <typename Handler> template <typename Handler>
bool operator()(Handler& handler) { bool operator()(Handler& handler) {
...@@ -2006,11 +2434,13 @@ public: ...@@ -2006,11 +2434,13 @@ public:
invalidSchemaPointer_ = PointerType(); invalidSchemaPointer_ = PointerType();
invalidSchemaKeyword_ = 0; invalidSchemaKeyword_ = 0;
invalidDocumentPointer_ = PointerType(); invalidDocumentPointer_ = PointerType();
error_.SetObject();
} }
else { else {
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
error_.CopyFrom(validator.GetError(), allocator_);
} }
return parseResult_; return parseResult_;
...@@ -2021,6 +2451,7 @@ public: ...@@ -2021,6 +2451,7 @@ public:
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
const ValueType& GetError() const { return error_; }
private: private:
InputStream& is_; InputStream& is_;
...@@ -2030,6 +2461,8 @@ private: ...@@ -2030,6 +2461,8 @@ private:
PointerType invalidSchemaPointer_; PointerType invalidSchemaPointer_;
const Ch* invalidSchemaKeyword_; const Ch* invalidSchemaKeyword_;
PointerType invalidDocumentPointer_; PointerType invalidDocumentPointer_;
StackAllocator allocator_;
ValueType error_;
bool isValid_; bool isValid_;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment