diff --git a/examples/custom_schema.cpp b/examples/custom_schema.cpp index 555601a..e69a41a 100644 --- a/examples/custom_schema.cpp +++ b/examples/custom_schema.cpp @@ -158,11 +158,11 @@ void addRequiredConstraint(Schema &schema) { // Add a RequiredConstraint to the schema, specifying that the category, // price, and title properties must be present. - RequiredConstraint::RequiredProperties requiredProperties; - requiredProperties.insert("category"); - requiredProperties.insert("price"); - requiredProperties.insert("title"); - schema.addConstraint(RequiredConstraint(requiredProperties)); + RequiredConstraint constraint; + constraint.addRequiredProperty("category"); + constraint.addRequiredProperty("price"); + constraint.addRequiredProperty("title"); + schema.addConstraint(constraint); } void addTypeConstraint(Schema &schema) diff --git a/include/valijson/constraints/concrete_constraints.hpp b/include/valijson/constraints/concrete_constraints.hpp index 82cdc20..ddbf1a6 100644 --- a/include/valijson/constraints/concrete_constraints.hpp +++ b/include/valijson/constraints/concrete_constraints.hpp @@ -552,17 +552,44 @@ struct PropertiesConstraint: BasicConstraint { /** * @brief Represents a 'required' constraint. */ -struct RequiredConstraint: BasicConstraint +class RequiredConstraint: public BasicConstraint { - typedef std::set RequiredProperties; +public: + RequiredConstraint() + : requiredProperties(std::less(), allocator) { } - RequiredConstraint(const RequiredProperties &requiredProperties) - : requiredProperties(requiredProperties) + RequiredConstraint(CustomAlloc allocFn, CustomFree freeFn) + : BasicConstraint(allocFn, freeFn), + requiredProperties(std::less(), allocator) { } + + bool addRequiredProperty(const char *propertyName) { - + return requiredProperties.insert(String(propertyName, + Allocator::rebind::other(allocator))).second; } - const RequiredProperties requiredProperties; + template + bool addRequiredProperty(const std::basic_string, AllocatorType> &propertyName) + { + return addRequiredProperty(propertyName.c_str()); + } + + template + void applyToRequiredProperties(const FunctorType &fn) const + { + BOOST_FOREACH( const String &propertyName, requiredProperties ) { + if (!fn(propertyName)) { + return; + } + } + } + +private: + typedef std::set, + internal::CustomAllocator > RequiredProperties; + + RequiredProperties requiredProperties; }; /** diff --git a/include/valijson/constraints/constraint_visitor.hpp b/include/valijson/constraints/constraint_visitor.hpp index ced9dec..ac8587e 100644 --- a/include/valijson/constraints/constraint_visitor.hpp +++ b/include/valijson/constraints/constraint_visitor.hpp @@ -19,13 +19,14 @@ struct MultipleOfConstraint; struct NotConstraint; struct PatternConstraint; struct PropertiesConstraint; -struct RequiredConstraint; + class AllOfConstraint; class AnyOfConstraint; class DependenciesConstraint; class LinearItemsConstraint; class OneOfConstraint; +class RequiredConstraint; class SingularItemsConstraint; class TypeConstraint; class UniqueItemsConstraint; diff --git a/include/valijson/schema_parser.hpp b/include/valijson/schema_parser.hpp index 9c01697..d1a7fc4 100644 --- a/include/valijson/schema_parser.hpp +++ b/include/valijson/schema_parser.hpp @@ -1372,9 +1372,9 @@ private: } if (node.asBool()) { - constraints::RequiredConstraint::RequiredProperties requiredProperties; - requiredProperties.insert(name); - return constraints::RequiredConstraint(requiredProperties); + constraints::RequiredConstraint constraint; + constraint.addRequiredProperty(name); + return constraint; } return boost::none; @@ -1395,15 +1395,18 @@ private: constraints::RequiredConstraint makeRequiredConstraint( const AdapterType &node) { - constraints::RequiredConstraint::RequiredProperties requiredProperties; + constraints::RequiredConstraint constraint; + BOOST_FOREACH( const AdapterType v, node.getArray() ) { if (!v.isString()) { - // @todo throw exception + throw std::runtime_error("Expected required property name to " + "be a string value"); } - requiredProperties.insert(v.getString()); + + constraint.addRequiredProperty(v.getString()); } - return constraints::RequiredConstraint(requiredProperties); + return constraint; } /** diff --git a/include/valijson/validation_visitor.hpp b/include/valijson/validation_visitor.hpp index 9337f5e..af07329 100644 --- a/include/valijson/validation_visitor.hpp +++ b/include/valijson/validation_visitor.hpp @@ -887,23 +887,16 @@ public: { if ((strictTypes && !target.isObject()) || !target.maybeObject()) { if (results) { - results->pushError(context, "Object required to validate 'required' properties."); + results->pushError(context, + "Object required to validate 'required' properties."); } return false; } bool validated = true; const typename AdapterType::Object object = target.asObject(); - BOOST_FOREACH( const std::string &requiredProperty, constraint.requiredProperties ) { - if (object.find(requiredProperty) == object.end()) { - if (results) { - results->pushError(context, "Missing required property '" + requiredProperty + "'."); - validated = false; - } else { - return false; - } - } - } + constraint.applyToRequiredProperties(ValidateProperties(object, context, + true, results != NULL, results, &validated)); return validated; } @@ -1053,6 +1046,53 @@ public: private: + /** + * @brief Functor to validate the presence of a set of properties + */ + struct ValidateProperties + { + ValidateProperties( + const typename AdapterType::Object &object, + const std::vector &context, + bool continueOnSuccess, + bool continueOnFailure, + ValidationResults *results, + bool *validated) + : object(object), + context(context), + continueOnSuccess(continueOnSuccess), + continueOnFailure(continueOnFailure), + results(results), + validated(validated) { } + + template + bool operator()(const StringType &property) const + { + if (object.find(property.c_str()) == object.end()) { + if (validated) { + *validated = false; + } + + if (results) { + results->pushError(context, "Missing required property '" + + std::string(property.c_str()) + "'."); + } + + return continueOnFailure; + } + + return continueOnSuccess; + } + + private: + const typename AdapterType::Object &object; + const std::vector &context; + bool continueOnSuccess; + bool continueOnFailure; + ValidationResults * const results; + bool * const validated; + }; + /** * @brief Functor to validate property-based dependencies */