From 314ee2281c3fb3935f1cae6752473669a0083c01 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 8 May 2015 13:37:30 +0800 Subject: [PATCH] Add multiple patternProperties match --- include/rapidjson/schema.h | 192 +++++++++++++++++++++++++---------- test/unittest/schematest.cpp | 1 + 2 files changed, 137 insertions(+), 56 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index bb42a3a..0821dba 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -82,15 +82,30 @@ struct BaseSchemaArray { SizeType count; }; +enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty +}; + template struct SchemaValidationContext { SchemaValidationContext(const BaseSchema* 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() { delete notValidator; +#if RAPIDJSON_SCHEMA_HAS_REGEX + delete patternPropertiesSchemas; +#endif delete[] objectDependencies; } @@ -100,6 +115,13 @@ struct SchemaValidationContext { SchemaValidatorArray anyOfValidators; SchemaValidatorArray oneOfValidators; SchemaValidatorArray dependencyValidators; +#if RAPIDJSON_SCHEMA_HAS_REGEX + SchemaValidatorArray patternPropertiesValidators; + const BaseSchema** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; +#endif GenericSchemaValidator, CrtAllocator>* notValidator; SizeType objectRequiredCount; SizeType arrayElementIndex; @@ -368,14 +390,43 @@ public: } 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) - for (SizeType i_ = 0; i_ < allOf_.count; i_++) - if (!context.allOfValidators.validators[i_]->IsValid()) + for (SizeType i = 0; i < allOf_.count; i++) + if (!context.allOfValidators.validators[i]->IsValid()) return false; if (anyOf_.schemas) { - for (SizeType i_ = 0; i_ < anyOf_.count; i_++) - if (context.anyOfValidators.validators[i_]->IsValid()) + for (SizeType i = 0; i < anyOf_.count; i++) + if (context.anyOfValidators.validators[i]->IsValid()) goto foundAny; return false; foundAny:; @@ -383,8 +434,8 @@ public: if (oneOf_.schemas) { bool oneValid = false; - for (SizeType i_ = 0; i_ < oneOf_.count; i_++) - if (context.oneOfValidators.validators[i_]->IsValid()) { + for (SizeType i = 0; i < oneOf_.count; i++) + if (context.oneOfValidators.validators[i]->IsValid()) { if (oneValid) return false; else @@ -398,21 +449,21 @@ public: } bool Null(Context& context) const { - CreateLogicValidators(context); + CreateParallelValidator(context); return (type_ & (1 << kNullSchemaType)) && (!enum_.IsArray() || CheckEnum(GenericValue().Move())); } bool Bool(Context& context, bool b) const { - CreateLogicValidators(context); + CreateParallelValidator(context); return (type_ & (1 << kBooleanSchemaType)) && (!enum_.IsArray() || CheckEnum(GenericValue(b).Move())); } bool Int(Context& context, int i) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) return false; @@ -420,7 +471,7 @@ public: } bool Uint(Context& context, unsigned u) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) return false; @@ -428,7 +479,7 @@ public: } bool Int64(Context& context, int64_t i) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) return false; @@ -436,7 +487,7 @@ public: } bool Uint64(Context& context, uint64_t u) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))) == 0) return false; @@ -444,7 +495,7 @@ public: } bool Double(Context& context, double d) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & (1 << kNumberSchemaType)) == 0) return false; @@ -453,7 +504,7 @@ public: bool String(Context& context, const Ch* str, SizeType length, bool) const { (void)str; - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & (1 << kStringSchemaType)) == 0) return false; @@ -474,7 +525,7 @@ public: } bool StartObject(Context& context) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & (1 << kObjectSchemaType)) == 0) return false; @@ -483,17 +534,37 @@ public: context.objectDependencies = new 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*[count]; + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(BaseSchema*) * count); + } + return true; } bool Key(Context& context, const Ch* str, SizeType len, bool) const { - CreateLogicValidators(context); - if ((type_ & (1 << kObjectSchemaType)) == 0) - return false; - +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (patternProperties_) { + 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; if (FindPropertyIndex(str, len, &index)) { - context.valueSchema = properties_[index].typeless ? GetTypeless() : properties_[index].schema; + const BaseSchema* 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) context.objectRequiredCount++; @@ -504,32 +575,29 @@ public: 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_) { - context.valueSchema = additionalPropertiesSchema_; + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; return true; } else if (additionalProperties_) { context.valueSchema = GetTypeless(); return true; } - else - return false; + +#if RAPIDJSON_SCHEMA_HAS_REGEX + return context.patternPropertiesSchemaCount != 0; // patternProperties are not additional properties +#else + return false; +#endif } bool EndObject(Context& context, SizeType memberCount) const { - CreateLogicValidators(context); - if ((type_ & (1 << kObjectSchemaType)) == 0) - return false; - if (context.objectRequiredCount != requiredCount_ || memberCount < minProperties_ || memberCount > maxProperties_) return false; @@ -551,7 +619,7 @@ public: } bool StartArray(Context& context) const { - CreateLogicValidators(context); + CreateParallelValidator(context); if ((type_ & (1 << kArraySchemaType)) == 0) return false; @@ -561,10 +629,6 @@ public: } bool EndArray(Context& context, SizeType elementCount) const { - CreateLogicValidators(context); - if ((type_ & (1 << kArraySchemaType)) == 0) - return false; - context.inArray = false; return elementCount >= minItems_ && elementCount <= maxItems_; } @@ -652,7 +716,7 @@ private: return false; } - void CreateLogicValidators(Context& context) const { + void CreateParallelValidator(Context& context) const { if (allOf_.schemas) CreateSchemaValidators(context.allOfValidators, allOf_); if (anyOf_.schemas) CreateSchemaValidators(context.anyOfValidators, anyOf_); if (oneOf_.schemas) CreateSchemaValidators(context.oneOfValidators, oneOf_); @@ -859,7 +923,7 @@ public: if (!valid_) return 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 <= schemaStack_.template Top(); context++) {\ if (context->allOfValidators.validators)\ for (SizeType i_ = 0; i_ < context->allOfValidators.count; i_++)\ @@ -876,15 +940,19 @@ public: for (SizeType i_ = 0; i_ < context->dependencyValidators.count; i_++)\ if (context->dependencyValidators.validators[i_])\ 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)\ return valid_ = EndValue() && outputHandler_.method arg2 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1);\ - RAPIDJSON_SCHEMA_HANDLE_LOGIC_(method, arg2);\ - RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } @@ -898,39 +966,39 @@ public: bool StartObject() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); - RAPIDJSON_SCHEMA_HANDLE_LOGIC_(StartObject, ()); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); return valid_ = outputHandler_.StartObject(); } bool Key(const Ch* str, SizeType len, bool copy) { if (!valid_) return 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); } bool EndObject(SizeType memberCount) { if (!valid_) return false; - RAPIDJSON_SCHEMA_HANDLE_LOGIC_(EndObject, (memberCount)); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; - RAPIDJSON_SCHEMA_HANDLE_END_ (EndObject, (memberCount)); + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); } bool StartArray() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); - RAPIDJSON_SCHEMA_HANDLE_LOGIC_(StartArray, ()); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); return valid_ = outputHandler_.StartArray(); } bool EndArray(SizeType elementCount) { if (!valid_) return false; - RAPIDJSON_SCHEMA_HANDLE_LOGIC_(EndArray, (elementCount)); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); 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_LOGIC_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ // Implementation of ISchemaValidatorFactory @@ -963,8 +1031,20 @@ private: if (!CurrentSchema().BeginValue(CurrentContext())) return false; + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const BaseSchemaType** sa = CurrentContext().patternPropertiesSchemas; + PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + if (CurrentContext().valueSchema) PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + SchemaValidatorArray& va = CurrentContext().patternPropertiesValidators; + va.validators = new GenericSchemaValidator*[count]; + for (SizeType i = 0; i < count; i++) + va.validators[va.count++] = CreateSchemaValidator(*sa[i]); + } } return true; } @@ -982,7 +1062,7 @@ private: const BaseSchemaType& CurrentSchema() { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } - static const size_t kDefaultSchemaStackCapacity = 256; + static const size_t kDefaultSchemaStackCapacity = 1024; //static const size_t kDefaultDocumentStackCapacity = 256; const BaseSchemaType& root_; BaseReaderHandler nullOutputHandler_; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 147200c..c4e04a4 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -666,6 +666,7 @@ static char* ReadFile(const char* filename, size_t& length) { TEST(SchemaValidator, TestSuite) { const char* filenames[] = { + "properties.json", "additionalItems.json", "additionalProperties.json", "allOf.json",