diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 4834326..03fc37c 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -44,15 +44,18 @@ class BaseSchema; template struct SchemaValidationContext { - SchemaValidationContext(const BaseSchema* s) : schema(s), valueSchema(), multiTypeSchema() {} + SchemaValidationContext(const BaseSchema* s) : schema(s), valueSchema(), multiTypeSchema(), objectDependencies() {} - ~SchemaValidationContext() {} + ~SchemaValidationContext() { + delete[] objectDependencies; + } const BaseSchema* schema; const BaseSchema* valueSchema; const BaseSchema* multiTypeSchema; SizeType objectRequiredCount; SizeType arrayElementIndex; + bool* objectDependencies; }; template @@ -246,7 +249,8 @@ public: requiredCount_(), minProperties_(), maxProperties_(SizeType(~0)), - additionalProperty_(true) + additionalProperty_(true), + hasDependencies_() { typename ValueType::ConstMemberIterator propretiesItr = value.FindMember("properties"); if (propretiesItr != value.MemberEnd()) { @@ -272,13 +276,53 @@ public: properties_[index].required = true; requiredCount_++; } + else { + // Error + } + } + else { + // Error } } + } + else { + // Error + } + } - if (requiredCount_ != requiredItr->value.Size()) { - // Error + // Establish dependencies after properties + typename ValueType::ConstMemberIterator dependenciesItr = value.FindMember("dependencies"); + if (dependenciesItr != value.MemberEnd()) { + if (dependenciesItr->value.IsObject()) { + hasDependencies_ = true; + for (typename ValueType::ConstMemberIterator itr = dependenciesItr->value.MemberBegin(); itr != dependenciesItr->value.MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + properties_[sourceIndex].dependencies = new bool[propertyCount_]; + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool) * propertyCount_); + if (itr->value.IsArray()) { + for (typename ValueType::ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) { + properties_[sourceIndex].dependencies[targetIndex] = true; + } + else { + // Error + } + } + } + else { + // Error + } + } + else { + // Error + } } } + else { + // Error + } } typename ValueType::ConstMemberIterator additionalPropretiesItr = value.FindMember("additionalProperties"); @@ -329,6 +373,10 @@ public: virtual bool StartObject(Context& context) const { context.objectRequiredCount = 0; + if (hasDependencies_) { + context.objectDependencies = new bool[propertyCount_]; + std::memset(context.objectDependencies, 0, sizeof(bool) * propertyCount_); + } return true; } @@ -340,6 +388,9 @@ public: if (properties_[index].required) context.objectRequiredCount++; + if (hasDependencies_) + context.objectDependencies[index] = true; + return true; } @@ -356,9 +407,18 @@ public: } virtual bool EndObject(Context& context, SizeType memberCount) const { - return context.objectRequiredCount == requiredCount_ && - memberCount >= minProperties_ && - memberCount <= maxProperties_; + if (context.objectRequiredCount != requiredCount_ || memberCount < minProperties_ || memberCount > maxProperties_) + return false; + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.objectDependencies[sourceIndex] && properties_[sourceIndex].dependencies) + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex]) + return false; + } + + return true; } virtual bool StartArray(Context&) const { return false; } @@ -391,13 +451,15 @@ private: } struct Property { - Property() : schema(), required(false) {} + Property() : schema(), dependencies(), required(false) {} ~Property() { delete schema; + delete[] dependencies; } GenericValue name; BaseSchema* schema; + bool* dependencies; bool required; }; @@ -409,6 +471,7 @@ private: SizeType minProperties_; SizeType maxProperties_; bool additionalProperty_; + bool hasDependencies_; }; template @@ -894,7 +957,6 @@ public: schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity) { - Reset(); } GenericSchemaValidator( @@ -909,11 +971,15 @@ public: schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity) { + } + + ~GenericSchemaValidator() { Reset(); } void Reset() { - schemaStack_.Clear(); + while (!schemaStack_.Empty()) + PopSchema(); documentStack_.Clear(); }; @@ -965,7 +1031,7 @@ private: } void PushSchema(const BaseSchemaType& schema) { *schemaStack_.template Push() = Context(&schema); } - const BaseSchemaType& PopSchema() { return *schemaStack_.template Pop(1)->schema; } + void PopSchema() { schemaStack_.template Pop(1)->~Context(); } const BaseSchemaType& CurrentSchema() { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 7c8f019..2d9476a 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -278,8 +278,6 @@ TEST(SchemaValidator, Object_PropertiesRange) { VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", false); } -#if 0 -// TODO TEST(SchemaValidator, Object_PropertyDependencies) { Document sd; sd.Parse( @@ -302,7 +300,6 @@ TEST(SchemaValidator, Object_PropertyDependencies) { VALIDATE(s, "{ \"name\": \"John Doe\"}", true); VALIDATE(s, "{ \"name\": \"John Doe\", \"billing_address\": \"555 Debtor's Lane\" }", true); } -#endif TEST(SchemaValidator, Array) { Document sd;