diff --git a/include/valijson/constraints/concrete_constraints.hpp b/include/valijson/constraints/concrete_constraints.hpp index fbbba00..9f35967 100644 --- a/include/valijson/constraints/concrete_constraints.hpp +++ b/include/valijson/constraints/concrete_constraints.hpp @@ -40,13 +40,11 @@ class AllOfConstraint: public BasicConstraint { public: AllOfConstraint() - : subschemas(Allocator::rebind::other(allocator)), - alwaysInvalid(false) { } + : subschemas(Allocator::rebind::other(allocator)) { } AllOfConstraint(CustomAlloc allocFn, CustomFree freeFn) : BasicConstraint(allocFn, freeFn), - subschemas(Allocator::rebind::other(allocator)), - alwaysInvalid(false) { } + subschemas(Allocator::rebind::other(allocator)) { } void addSubschema(const Subschema *subschema) { @@ -66,24 +64,12 @@ public: } } - bool getAlwaysInvalid() const - { - return alwaysInvalid; - } - - void setAlwaysInvalid() - { - alwaysInvalid = true; - } - private: typedef std::vector > Subschemas; /// Collection of sub-schemas, all of which must be satisfied Subschemas subschemas; - - bool alwaysInvalid; }; /** @@ -97,8 +83,7 @@ class AnyOfConstraint: public BasicConstraint { public: AnyOfConstraint() - : subschemas(Allocator::rebind::other(allocator)), - alwaysValid(false) { } + : subschemas(Allocator::rebind::other(allocator)) { } AnyOfConstraint(CustomAlloc allocFn, CustomFree freeFn) : BasicConstraint(allocFn, freeFn), @@ -122,24 +107,12 @@ public: } } - bool getAlwaysValid() const - { - return alwaysValid; - } - - void setAlwaysValid() - { - alwaysValid = true; - } - private: typedef std::vector > Subschemas; /// Collection of sub-schemas, at least one of which must be satisfied Subschemas subschemas; - - bool alwaysValid; }; class ConditionalConstraint: public BasicConstraint diff --git a/include/valijson/schema.hpp b/include/valijson/schema.hpp index 4620da7..d0700c7 100644 --- a/include/valijson/schema.hpp +++ b/include/valijson/schema.hpp @@ -118,6 +118,11 @@ public: return this; } + void setAlwaysInvalid(const Subschema *subschema, bool value) + { + mutableSubschema(subschema)->setAlwaysInvalid(value); + } + /** * @brief Update the description for one of the sub-schemas owned by this * Schema instance diff --git a/include/valijson/schema_parser.hpp b/include/valijson/schema_parser.hpp index 30f3b2e..5319eda 100644 --- a/include/valijson/schema_parser.hpp +++ b/include/valijson/schema_parser.hpp @@ -594,12 +594,24 @@ private: "appropriate Adapter implementation"); if (!node.isObject()) { - std::string s; - s += "Expected node at "; - s += nodePath; - s += " to contain schema object; actual node type is: "; - s += internal::nodeTypeAsString(node); - throw std::runtime_error(s); + if (version == kDraft7 && node.maybeBool()) { + // Boolean schema + if (!node.asBool()) { + rootSchema.setAlwaysInvalid(&subschema, true); + } + return; + } else { + std::string s; + s += "Expected node at "; + s += nodePath; + if (version == kDraft7) { + s += " to contain schema object or boolean value; actual node type is: "; + } else { + s += " to contain schema object; actual node type is: "; + } + s += internal::nodeTypeAsString(node); + throw std::runtime_error(s); + } } const typename AdapterType::Object object = node.asObject(); @@ -1095,26 +1107,16 @@ private: int index = 0; for (const AdapterType schemaNode : node.asArray()) { - if (schemaNode.maybeObject()) { + if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) { const std::string childPath = nodePath + "/" + std::to_string(index); const Subschema *subschema = makeOrReuseSchema( rootSchema, rootNode, schemaNode, currentScope, childPath, fetchDoc, NULL, NULL, docCache, schemaCache); constraint.addSubschema(subschema); index++; - } else if (version == kDraft7) { - if (schemaNode.maybeBool()) { - if (!schemaNode.asBool()) { - // Schema that always fails - constraint.setAlwaysInvalid(); - } - } else { - throw std::runtime_error( - "Expected element to be an object or boolean value in 'allOf' constraint"); - } } else { throw std::runtime_error( - "Expected element to be an object value in 'allOf' constraint."); + "Expected element to be a valid schema in 'allOf' constraint."); } } @@ -1159,25 +1161,16 @@ private: int index = 0; for (const AdapterType schemaNode : node.asArray()) { - if (schemaNode.maybeObject()) { + if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) { const std::string childPath = nodePath + "/" + std::to_string(index); const Subschema *subschema = makeOrReuseSchema( rootSchema, rootNode, schemaNode, currentScope, childPath, fetchDoc, NULL, NULL, docCache, schemaCache); constraint.addSubschema(subschema); index++; - } else if (version == kDraft7) { - if (schemaNode.maybeBool()) { - if (schemaNode.asBool()) { - constraint.setAlwaysValid(); - } - } else { - throw std::runtime_error( - "Expected element to be an object or boolean value in 'allOf' constraint"); - } } else { throw std::runtime_error( - "Expected array element to be an object value in 'anyOf' constraint."); + "Expected array element to be a valid schema in 'anyOf' constraint."); } } @@ -1270,7 +1263,7 @@ private: SchemaCache &schemaCache) { if (!node.maybeObject()) { - throw std::runtime_error("Expected object value for 'dependencies' constraint."); + throw std::runtime_error("Expected valid subschema for 'dependencies' constraint."); } constraints::DependenciesConstraint dependenciesConstraint; @@ -1508,7 +1501,7 @@ private: // array is provided, or a single Schema object, in an object value is // provided. If the items constraint is not provided, then array items // will be validated against the additionalItems schema. - if (items.isObject()) { + if (items.isObject() || (version == kDraft7 && items.maybeBool())) { // If the items constraint contains an object value, then it // should contain a Schema that will be used to validate all // items in a target array. Any schema defined by the @@ -1526,8 +1519,7 @@ private: } else { // All other formats will result in an exception being thrown. throw std::runtime_error( - "Expected object value for singular 'items' " - "constraint."); + "Expected valid schema for singular 'items' constraint."); } return constraint; @@ -1883,7 +1875,7 @@ private: typename DocumentCache::Type &docCache, SchemaCache &schemaCache) { - if (node.maybeObject()) { + if (node.maybeObject() || (version == kDraft7 && node.maybeBool())) { const Subschema *subschema = makeOrReuseSchema( rootSchema, rootNode, node, currentScope, nodePath, fetchDoc, NULL, NULL, docCache, schemaCache); diff --git a/include/valijson/subschema.hpp b/include/valijson/subschema.hpp index dc38a3d..d4b45e6 100644 --- a/include/valijson/subschema.hpp +++ b/include/valijson/subschema.hpp @@ -40,7 +40,8 @@ public: */ Subschema() : allocFn(::operator new) - , freeFn(::operator delete) { } + , freeFn(::operator delete) + , alwaysInvalid(false) { } /** * @brief Construct a new Subschema using custom memory management @@ -53,7 +54,8 @@ public: */ Subschema(CustomAlloc allocFn, CustomFree freeFn) : allocFn(allocFn) - , freeFn(freeFn) { } + , freeFn(freeFn) + , alwaysInvalid(false) { } /** * @brief Clean up and free all memory managed by the Subschema @@ -139,6 +141,11 @@ public: return true; } + bool getAlwaysInvalid() const + { + return alwaysInvalid; + } + /** * @brief Get the description associated with this sub-schema * @@ -217,6 +224,11 @@ public: return static_cast(title); } + void setAlwaysInvalid(bool value) + { + alwaysInvalid = value; + } + /** * @brief Set the description for this sub-schema * @@ -266,6 +278,8 @@ private: // Disable copy assignment Subschema & operator=(const Subschema &); + bool alwaysInvalid; + /// List of pointers to constraints that apply to this schema. std::vector constraints; diff --git a/include/valijson/validation_visitor.hpp b/include/valijson/validation_visitor.hpp index 78c37c7..c203755 100644 --- a/include/valijson/validation_visitor.hpp +++ b/include/valijson/validation_visitor.hpp @@ -63,6 +63,10 @@ public: */ bool validateSchema(const Subschema &subschema) { + if (subschema.getAlwaysInvalid()) { + return false; + } + // Wrap the validationCallback() function below so that it will be // passed a reference to a constraint (_1), and a reference to the // visitor (*this). @@ -108,10 +112,6 @@ public: */ virtual bool visit(const AllOfConstraint &constraint) { - if (constraint.getAlwaysInvalid()) { - return false; - } - bool validated = true; constraint.applyToSubschemas(ValidateSubschemas(target, context, true, false, *this, results, NULL, &validated)); @@ -138,10 +138,6 @@ public: */ virtual bool visit(const AnyOfConstraint &constraint) { - if (constraint.getAlwaysValid()) { - return true; - } - unsigned int numValidated = 0; ValidationResults newResults; diff --git a/tests/test_validator.cpp b/tests/test_validator.cpp index a66188a..e700388 100644 --- a/tests/test_validator.cpp +++ b/tests/test_validator.cpp @@ -446,7 +446,10 @@ TEST_F(TestValidator, Draft7_AnyOf) processDraft7TestFile(TEST_SUITE_DIR "draft7/anyOf.json"); } -// TODO: untested boolean_schema +TEST_F(TestValidator, Draft7_BooleanSchema) +{ + processDraft7TestFile(TEST_SUITE_DIR "draft7/boolean_schema.json"); +} // TODO: untested const @@ -476,7 +479,10 @@ TEST_F(TestValidator, Draft7_IfThenElse) processDraft7TestFile(TEST_SUITE_DIR "draft7/if-then-else.json"); } -// TODO: broken items +TEST_F(TestValidator, Draft7_Items) +{ + processDraft7TestFile(TEST_SUITE_DIR "draft7/items.json"); +} TEST_F(TestValidator, Draft7_Maximum) { @@ -513,18 +519,30 @@ TEST_F(TestValidator, Draft7_MultipleOf) processDraft7TestFile(TEST_SUITE_DIR "draft7/multipleOf.json"); } -// TODO: broken not +TEST_F(TestValidator, Draft7_Not) +{ + processDraft7TestFile(TEST_SUITE_DIR "draft7/not.json"); +} -// TODO: broken oneOf +TEST_F(TestValidator, Draft7_OneOf) +{ + processDraft7TestFile(TEST_SUITE_DIR "draft7/oneOf.json"); +} TEST_F(TestValidator, Draft7_Pattern) { processDraft7TestFile(TEST_SUITE_DIR "draft7/pattern.json"); } -// TODO: broken patternProperties +TEST_F(TestValidator, Draft7_PatternProperties) +{ + processDraft7TestFile(TEST_SUITE_DIR "draft7/patternProperties.json"); +} -// TODO: broken properties +TEST_F(TestValidator, Draft7_Properties) +{ + processDraft7TestFile(TEST_SUITE_DIR "draft7/properties.json"); +} // TODO: broken ref