Commit 314ee228 authored by Milo Yip's avatar Milo Yip

Add multiple patternProperties match

parent 3d3555d3
...@@ -82,15 +82,30 @@ struct BaseSchemaArray { ...@@ -82,15 +82,30 @@ struct BaseSchemaArray {
SizeType count; SizeType count;
}; };
enum PatternValidatorType {
kPatternValidatorOnly,
kPatternValidatorWithProperty,
kPatternValidatorWithAdditionalProperty
};
template <typename Encoding> template <typename Encoding>
struct SchemaValidationContext { struct SchemaValidationContext {
SchemaValidationContext(const BaseSchema<Encoding>* s) : SchemaValidationContext(const BaseSchema<Encoding>* s) :
schema(s), valueSchema(), notValidator(), objectDependencies(), inArray(false) schema(s), valueSchema(), notValidator(), objectDependencies(),
#if RAPIDJSON_SCHEMA_HAS_REGEX
patternPropertiesSchemas(),
patternPropertiesSchemaCount(),
valuePatternValidatorType(kPatternValidatorOnly),
#endif
inArray(false)
{ {
} }
~SchemaValidationContext() { ~SchemaValidationContext() {
delete notValidator; delete notValidator;
#if RAPIDJSON_SCHEMA_HAS_REGEX
delete patternPropertiesSchemas;
#endif
delete[] objectDependencies; delete[] objectDependencies;
} }
...@@ -100,6 +115,13 @@ struct SchemaValidationContext { ...@@ -100,6 +115,13 @@ struct SchemaValidationContext {
SchemaValidatorArray<Encoding> anyOfValidators; SchemaValidatorArray<Encoding> anyOfValidators;
SchemaValidatorArray<Encoding> oneOfValidators; SchemaValidatorArray<Encoding> oneOfValidators;
SchemaValidatorArray<Encoding> dependencyValidators; SchemaValidatorArray<Encoding> dependencyValidators;
#if RAPIDJSON_SCHEMA_HAS_REGEX
SchemaValidatorArray<Encoding> patternPropertiesValidators;
const BaseSchema<Encoding>** patternPropertiesSchemas;
SizeType patternPropertiesSchemaCount;
PatternValidatorType valuePatternValidatorType;
PatternValidatorType objectPatternValidatorType;
#endif
GenericSchemaValidator<Encoding, BaseReaderHandler<>, CrtAllocator>* notValidator; GenericSchemaValidator<Encoding, BaseReaderHandler<>, CrtAllocator>* notValidator;
SizeType objectRequiredCount; SizeType objectRequiredCount;
SizeType arrayElementIndex; SizeType arrayElementIndex;
...@@ -368,14 +390,43 @@ public: ...@@ -368,14 +390,43 @@ public:
} }
bool EndValue(Context& context) const { bool EndValue(Context& context) const {
if (context.patternPropertiesValidators.count > 0) {
bool otherValid = false;
SizeType count = context.patternPropertiesValidators.count;
if (context.objectPatternValidatorType != kPatternValidatorOnly)
otherValid = context.patternPropertiesValidators.validators[--count]->IsValid();
bool patternValid = true;
for (SizeType i = 0; i < count; i++)
if (!context.patternPropertiesValidators.validators[i]->IsValid()) {
patternValid = false;
break;
}
switch (context.objectPatternValidatorType) {
case kPatternValidatorOnly:
if (!patternValid)
return false;
break;
case kPatternValidatorWithProperty:
if (!patternValid || !otherValid)
return false;
break;
case kPatternValidatorWithAdditionalProperty:
if (!patternValid && !otherValid)
return false;
break;
}
}
if (allOf_.schemas) if (allOf_.schemas)
for (SizeType i_ = 0; i_ < allOf_.count; i_++) for (SizeType i = 0; i < allOf_.count; i++)
if (!context.allOfValidators.validators[i_]->IsValid()) if (!context.allOfValidators.validators[i]->IsValid())
return false; return false;
if (anyOf_.schemas) { if (anyOf_.schemas) {
for (SizeType i_ = 0; i_ < anyOf_.count; i_++) for (SizeType i = 0; i < anyOf_.count; i++)
if (context.anyOfValidators.validators[i_]->IsValid()) if (context.anyOfValidators.validators[i]->IsValid())
goto foundAny; goto foundAny;
return false; return false;
foundAny:; foundAny:;
...@@ -383,8 +434,8 @@ public: ...@@ -383,8 +434,8 @@ public:
if (oneOf_.schemas) { if (oneOf_.schemas) {
bool oneValid = false; bool oneValid = false;
for (SizeType i_ = 0; i_ < oneOf_.count; i_++) for (SizeType i = 0; i < oneOf_.count; i++)
if (context.oneOfValidators.validators[i_]->IsValid()) { if (context.oneOfValidators.validators[i]->IsValid()) {
if (oneValid) if (oneValid)
return false; return false;
else else
...@@ -398,21 +449,21 @@ public: ...@@ -398,21 +449,21 @@ public:
} }
bool Null(Context& context) const { bool Null(Context& context) const {
CreateLogicValidators(context); CreateParallelValidator(context);
return return
(type_ & (1 << kNullSchemaType)) && (type_ & (1 << kNullSchemaType)) &&
(!enum_.IsArray() || CheckEnum(GenericValue<Encoding>().Move())); (!enum_.IsArray() || CheckEnum(GenericValue<Encoding>().Move()));
} }
bool Bool(Context& context, bool b) const { bool Bool(Context& context, bool b) const {
CreateLogicValidators(context); CreateParallelValidator(context);
return return
(type_ & (1 << kBooleanSchemaType)) && (type_ & (1 << kBooleanSchemaType)) &&
(!enum_.IsArray() || CheckEnum(GenericValue<Encoding>(b).Move())); (!enum_.IsArray() || CheckEnum(GenericValue<Encoding>(b).Move()));
} }
bool Int(Context& context, int i) const { bool Int(Context& context, int i) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0)
return false; return false;
...@@ -420,7 +471,7 @@ public: ...@@ -420,7 +471,7 @@ public:
} }
bool Uint(Context& context, unsigned u) const { bool Uint(Context& context, unsigned u) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0)
return false; return false;
...@@ -428,7 +479,7 @@ public: ...@@ -428,7 +479,7 @@ public:
} }
bool Int64(Context& context, int64_t i) const { bool Int64(Context& context, int64_t i) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0)
return false; return false;
...@@ -436,7 +487,7 @@ public: ...@@ -436,7 +487,7 @@ public:
} }
bool Uint64(Context& context, uint64_t u) const { bool Uint64(Context& context, uint64_t u) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0)
return false; return false;
...@@ -444,7 +495,7 @@ public: ...@@ -444,7 +495,7 @@ public:
} }
bool Double(Context& context, double d) const { bool Double(Context& context, double d) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & (1 << kNumberSchemaType)) == 0) if ((type_ & (1 << kNumberSchemaType)) == 0)
return false; return false;
...@@ -453,7 +504,7 @@ public: ...@@ -453,7 +504,7 @@ public:
bool String(Context& context, const Ch* str, SizeType length, bool) const { bool String(Context& context, const Ch* str, SizeType length, bool) const {
(void)str; (void)str;
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & (1 << kStringSchemaType)) == 0) if ((type_ & (1 << kStringSchemaType)) == 0)
return false; return false;
...@@ -474,7 +525,7 @@ public: ...@@ -474,7 +525,7 @@ public:
} }
bool StartObject(Context& context) const { bool StartObject(Context& context) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & (1 << kObjectSchemaType)) == 0) if ((type_ & (1 << kObjectSchemaType)) == 0)
return false; return false;
...@@ -483,17 +534,37 @@ public: ...@@ -483,17 +534,37 @@ public:
context.objectDependencies = new bool[propertyCount_]; context.objectDependencies = new bool[propertyCount_];
std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_); std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_);
} }
if (patternProperties_) { // pre-allocate schema array
SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
context.patternPropertiesSchemas = new const BaseSchema<Encoding>*[count];
context.patternPropertiesSchemaCount = 0;
std::memset(context.patternPropertiesSchemas, 0, sizeof(BaseSchema<Encoding>*) * count);
}
return true; return true;
} }
bool Key(Context& context, const Ch* str, SizeType len, bool) const { bool Key(Context& context, const Ch* str, SizeType len, bool) const {
CreateLogicValidators(context); #if RAPIDJSON_SCHEMA_HAS_REGEX
if ((type_ & (1 << kObjectSchemaType)) == 0) if (patternProperties_) {
return false; context.patternPropertiesSchemaCount = 0;
for (SizeType i = 0; i < patternPropertyCount_; i++)
if (patternProperties_[i].pattern && IsPatternMatch(*patternProperties_[i].pattern, str, len))
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
}
#endif
SizeType index; SizeType index;
if (FindPropertyIndex(str, len, &index)) { if (FindPropertyIndex(str, len, &index)) {
context.valueSchema = properties_[index].typeless ? GetTypeless() : properties_[index].schema; const BaseSchema<Encoding>* propertySchema = properties_[index].typeless ? GetTypeless() : properties_[index].schema;
if (context.patternPropertiesSchemaCount > 0) {
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = propertySchema;
context.valueSchema = GetTypeless();
context.valuePatternValidatorType = kPatternValidatorWithProperty;
}
else
context.valueSchema = propertySchema;
if (properties_[index].required) if (properties_[index].required)
context.objectRequiredCount++; context.objectRequiredCount++;
...@@ -504,16 +575,13 @@ public: ...@@ -504,16 +575,13 @@ public:
return true; return true;
} }
#if RAPIDJSON_SCHEMA_HAS_REGEX
if (patternProperties_)
for (SizeType i = 0; i < patternPropertyCount_; i++)
if (patternProperties_[i].pattern && IsPatternMatch(*patternProperties_[i].pattern, str, len)) {
context.valueSchema = patternProperties_[i].schema;
return true;
}
#endif
if (additionalPropertiesSchema_) { if (additionalPropertiesSchema_) {
if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
context.valueSchema = GetTypeless();
context.valuePatternValidatorType = kPatternValidatorWithAdditionalProperty;
}
else
context.valueSchema = additionalPropertiesSchema_; context.valueSchema = additionalPropertiesSchema_;
return true; return true;
} }
...@@ -521,15 +589,15 @@ public: ...@@ -521,15 +589,15 @@ public:
context.valueSchema = GetTypeless(); context.valueSchema = GetTypeless();
return true; return true;
} }
else
#if RAPIDJSON_SCHEMA_HAS_REGEX
return context.patternPropertiesSchemaCount != 0; // patternProperties are not additional properties
#else
return false; return false;
#endif
} }
bool EndObject(Context& context, SizeType memberCount) const { bool EndObject(Context& context, SizeType memberCount) const {
CreateLogicValidators(context);
if ((type_ & (1 << kObjectSchemaType)) == 0)
return false;
if (context.objectRequiredCount != requiredCount_ || memberCount < minProperties_ || memberCount > maxProperties_) if (context.objectRequiredCount != requiredCount_ || memberCount < minProperties_ || memberCount > maxProperties_)
return false; return false;
...@@ -551,7 +619,7 @@ public: ...@@ -551,7 +619,7 @@ public:
} }
bool StartArray(Context& context) const { bool StartArray(Context& context) const {
CreateLogicValidators(context); CreateParallelValidator(context);
if ((type_ & (1 << kArraySchemaType)) == 0) if ((type_ & (1 << kArraySchemaType)) == 0)
return false; return false;
...@@ -561,10 +629,6 @@ public: ...@@ -561,10 +629,6 @@ public:
} }
bool EndArray(Context& context, SizeType elementCount) const { bool EndArray(Context& context, SizeType elementCount) const {
CreateLogicValidators(context);
if ((type_ & (1 << kArraySchemaType)) == 0)
return false;
context.inArray = false; context.inArray = false;
return elementCount >= minItems_ && elementCount <= maxItems_; return elementCount >= minItems_ && elementCount <= maxItems_;
} }
...@@ -652,7 +716,7 @@ private: ...@@ -652,7 +716,7 @@ private:
return false; return false;
} }
void CreateLogicValidators(Context& context) const { void CreateParallelValidator(Context& context) const {
if (allOf_.schemas) CreateSchemaValidators(context.allOfValidators, allOf_); if (allOf_.schemas) CreateSchemaValidators(context.allOfValidators, allOf_);
if (anyOf_.schemas) CreateSchemaValidators(context.anyOfValidators, anyOf_); if (anyOf_.schemas) CreateSchemaValidators(context.anyOfValidators, anyOf_);
if (oneOf_.schemas) CreateSchemaValidators(context.oneOfValidators, oneOf_); if (oneOf_.schemas) CreateSchemaValidators(context.oneOfValidators, oneOf_);
...@@ -859,7 +923,7 @@ public: ...@@ -859,7 +923,7 @@ public:
if (!valid_) return false; \ if (!valid_) return false; \
if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false; if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false;
#define RAPIDJSON_SCHEMA_HANDLE_LOGIC_(method, arg2)\ #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
for (Context* context = schemaStack_.template Bottom<Context>(); context <= schemaStack_.template Top<Context>(); context++) {\ for (Context* context = schemaStack_.template Bottom<Context>(); context <= schemaStack_.template Top<Context>(); context++) {\
if (context->allOfValidators.validators)\ if (context->allOfValidators.validators)\
for (SizeType i_ = 0; i_ < context->allOfValidators.count; i_++)\ for (SizeType i_ = 0; i_ < context->allOfValidators.count; i_++)\
...@@ -876,14 +940,18 @@ public: ...@@ -876,14 +940,18 @@ public:
for (SizeType i_ = 0; i_ < context->dependencyValidators.count; i_++)\ for (SizeType i_ = 0; i_ < context->dependencyValidators.count; i_++)\
if (context->dependencyValidators.validators[i_])\ if (context->dependencyValidators.validators[i_])\
context->dependencyValidators.validators[i_]->method arg2;\ context->dependencyValidators.validators[i_]->method arg2;\
if (context->patternPropertiesValidators.validators)\
for (SizeType i_ = 0; i_ < context->patternPropertiesValidators.count; i_++)\
if (context->patternPropertiesValidators.validators[i_])\
context->patternPropertiesValidators.validators[i_]->method arg2; \
} }
#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
return valid_ = EndValue() && outputHandler_.method arg2 return valid_ = EndValue() && outputHandler_.method arg2
#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1);\ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
RAPIDJSON_SCHEMA_HANDLE_LOGIC_(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() ), ( )); }
...@@ -898,39 +966,39 @@ public: ...@@ -898,39 +966,39 @@ public:
bool StartObject() { bool StartObject() {
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
RAPIDJSON_SCHEMA_HANDLE_LOGIC_(StartObject, ()); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
return valid_ = outputHandler_.StartObject(); return valid_ = outputHandler_.StartObject();
} }
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;
if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_LOGIC_(Key, (str, len, copy)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
return valid_ = outputHandler_.Key(str, len, copy); return valid_ = outputHandler_.Key(str, len, copy);
} }
bool EndObject(SizeType memberCount) { bool EndObject(SizeType memberCount) {
if (!valid_) return false; if (!valid_) return false;
RAPIDJSON_SCHEMA_HANDLE_LOGIC_(EndObject, (memberCount)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; if (!CurrentSchema().EndObject(CurrentContext(), 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()));
RAPIDJSON_SCHEMA_HANDLE_LOGIC_(StartArray, ()); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
return valid_ = outputHandler_.StartArray(); return valid_ = outputHandler_.StartArray();
} }
bool EndArray(SizeType elementCount) { bool EndArray(SizeType elementCount) {
if (!valid_) return false; if (!valid_) return false;
RAPIDJSON_SCHEMA_HANDLE_LOGIC_(EndArray, (elementCount)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_END_ (EndArray, (elementCount)); RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
} }
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
#undef RAPIDJSON_SCHEMA_HANDLE_LOGIC_ #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
// Implementation of ISchemaValidatorFactory<Encoding> // Implementation of ISchemaValidatorFactory<Encoding>
...@@ -963,8 +1031,20 @@ private: ...@@ -963,8 +1031,20 @@ private:
if (!CurrentSchema().BeginValue(CurrentContext())) if (!CurrentSchema().BeginValue(CurrentContext()))
return false; return false;
SizeType count = CurrentContext().patternPropertiesSchemaCount;
const BaseSchemaType** sa = CurrentContext().patternPropertiesSchemas;
PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
if (CurrentContext().valueSchema) if (CurrentContext().valueSchema)
PushSchema(*CurrentContext().valueSchema); PushSchema(*CurrentContext().valueSchema);
if (count > 0) {
CurrentContext().objectPatternValidatorType = patternValidatorType;
SchemaValidatorArray<Encoding>& va = CurrentContext().patternPropertiesValidators;
va.validators = new GenericSchemaValidator*[count];
for (SizeType i = 0; i < count; i++)
va.validators[va.count++] = CreateSchemaValidator(*sa[i]);
}
} }
return true; return true;
} }
...@@ -982,7 +1062,7 @@ private: ...@@ -982,7 +1062,7 @@ private:
const BaseSchemaType& CurrentSchema() { return *schemaStack_.template Top<Context>()->schema; } const BaseSchemaType& CurrentSchema() { return *schemaStack_.template Top<Context>()->schema; }
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); } Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
static const size_t kDefaultSchemaStackCapacity = 256; static const size_t kDefaultSchemaStackCapacity = 1024;
//static const size_t kDefaultDocumentStackCapacity = 256; //static const size_t kDefaultDocumentStackCapacity = 256;
const BaseSchemaType& root_; const BaseSchemaType& root_;
BaseReaderHandler<Encoding> nullOutputHandler_; BaseReaderHandler<Encoding> nullOutputHandler_;
......
...@@ -666,6 +666,7 @@ static char* ReadFile(const char* filename, size_t& length) { ...@@ -666,6 +666,7 @@ static char* ReadFile(const char* filename, size_t& length) {
TEST(SchemaValidator, TestSuite) { TEST(SchemaValidator, TestSuite) {
const char* filenames[] = { const char* filenames[] = {
"properties.json",
"additionalItems.json", "additionalItems.json",
"additionalProperties.json", "additionalProperties.json",
"allOf.json", "allOf.json",
......
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