Implement multipleOf constraint using std::remainder

This commit is contained in:
Tristan Penman 2015-03-17 21:23:17 +11:00
parent 27c065b37f
commit b377609efa
5 changed files with 148 additions and 11 deletions

View File

@ -263,16 +263,25 @@ struct MinPropertiesConstraint: BasicConstraint<MinPropertiesConstraint>
};
/**
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint.
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint for decimals
*/
struct MultipleOfConstraint: BasicConstraint<MultipleOfConstraint>
struct MultipleOfDecimalConstraint: BasicConstraint<MultipleOfDecimalConstraint>
{
MultipleOfConstraint(double multipleOf)
: multipleOf(multipleOf),
epsilon(std::numeric_limits<double>::epsilon()) { }
MultipleOfDecimalConstraint(double multipleOf)
: multipleOf(multipleOf) { }
const double multipleOf;
const double epsilon;
};
/**
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint for int64_t
*/
struct MultipleOfIntegerConstraint: BasicConstraint<MultipleOfIntegerConstraint>
{
MultipleOfIntegerConstraint(int64_t multipleOf)
: multipleOf(multipleOf) { }
const int64_t multipleOf;
};
/**

View File

@ -18,7 +18,8 @@ struct MinimumConstraint;
struct MinItemsConstraint;
struct MinLengthConstraint;
struct MinPropertiesConstraint;
struct MultipleOfConstraint;
struct MultipleOfDecimalConstraint;
struct MultipleOfIntegerConstraint;
struct NotConstraint;
struct OneOfConstraint;
struct PatternConstraint;
@ -45,7 +46,8 @@ protected:
typedef constraints::MinItemsConstraint MinItemsConstraint;
typedef constraints::MinLengthConstraint MinLengthConstraint;
typedef constraints::MinPropertiesConstraint MinPropertiesConstraint;
typedef constraints::MultipleOfConstraint MultipleOfConstraint;
typedef constraints::MultipleOfDecimalConstraint MultipleOfDecimalConstraint;
typedef constraints::MultipleOfIntegerConstraint MultipleOfIntegerConstraint;
typedef constraints::NotConstraint NotConstraint;
typedef constraints::OneOfConstraint OneOfConstraint;
typedef constraints::PatternConstraint PatternConstraint;
@ -69,7 +71,8 @@ public:
virtual bool visit(const MinItemsConstraint &) = 0;
virtual bool visit(const MinLengthConstraint &) = 0;
virtual bool visit(const MinPropertiesConstraint &) = 0;
virtual bool visit(const MultipleOfConstraint &) = 0;
virtual bool visit(const MultipleOfDecimalConstraint &) = 0;
virtual bool visit(const MultipleOfIntegerConstraint &) = 0;
virtual bool visit(const NotConstraint &) = 0;
virtual bool visit(const OneOfConstraint &) = 0;
virtual bool visit(const PatternConstraint &) = 0;

View File

@ -167,6 +167,10 @@ public:
schema.addConstraint(makeMinPropertiesConstraint(itr->second));
}
if ((itr = object.find("multipleOf")) != object.end()) {
schema.addConstraint(makeMultipleOfConstraint(itr->second));
}
if ((itr = object.find("not")) != object.end()) {
schema.addConstraint(makeNotConstraint(itr->second, deref));
}
@ -778,6 +782,31 @@ private:
throw std::runtime_error("Expected a positive integer for 'minProperties' constraint.");
}
/**
* @brief Make a new MultipleOfConstraint object.
*
* @param node JSON node containing an integer value that a value must
* be divisible by.
*
* @return pointer to a new MultipleOfConstraint that belongs to the
* caller
*/
template<typename AdapterType>
constraints::Constraint* makeMultipleOfConstraint(
const AdapterType &node)
{
// Allow both integral and double types to be provided
if (node.maybeInteger()) {
return new constraints::MultipleOfIntegerConstraint(
node.asInteger());
} else if (node.maybeDouble()) {
return new constraints::MultipleOfDecimalConstraint(
node.asDouble());
}
throw std::runtime_error("Expected an numeric value for 'multipleOf' constraint.");
}
/**
* @brief Make a new NotConstraint object.
*

View File

@ -1,6 +1,8 @@
#ifndef __VALIJSON_VALIDATION_VISITOR_HPP
#define __VALIJSON_VALIDATION_VISITOR_HPP
#include <numeric>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
@ -618,7 +620,7 @@ public:
/**
* @brief Validate against the multipleOf or divisibleBy constraints
* represented by a MultipleOfConstraint object.
* represented by a MultipleOfDecimalConstraint object.
*
* @todo Not implemented.
*
@ -626,8 +628,97 @@ public:
*
* @return true if the constraint is satisfied, false otherwise.
*/
virtual bool visit(const MultipleOfConstraint &constraint)
virtual bool visit(const MultipleOfDecimalConstraint &constraint)
{
double d = 0.;
if (target.maybeDouble()) {
if (!target.asDouble(d)) {
if (results) {
results->pushError(context, "Value could not be converted "
"to a number for multipleOf check");
}
return false;
}
} else if (target.maybeInteger()) {
int64_t i = 0;
if (!target.asInteger(i)) {
if (results) {
results->pushError(context, "Value could not be converted "
"to a number for multipleOf check");
}
return false;
}
d = i;
} else {
return true;
}
if (d == 0) {
return true;
}
const double r = std::remainder(d, constraint.multipleOf);
if (fabs(r) > std::numeric_limits<double>::epsilon()) {
if (results) {
results->pushError(context, "Value should be a multiple of " +
boost::lexical_cast<std::string>(constraint.multipleOf));
}
return false;
}
return true;
}
/**
* @brief Validate against the multipleOf or divisibleBy constraints
* represented by a MultipleOfIntegerConstraint object.
*
* @todo Not implemented.
*
* @param constraint Constraint that the target must validate against.
*
* @return true if the constraint is satisfied, false otherwise.
*/
virtual bool visit(const MultipleOfIntegerConstraint &constraint)
{
int64_t i = 0;
if (target.maybeInteger()) {
if (!target.asInteger(i)) {
if (results) {
results->pushError(context, "Value could not be converted "
"to an integer for multipleOf check");
}
return false;
}
} else if (target.maybeDouble()) {
double d;
if (!target.asDouble(d)) {
if (results) {
results->pushError(context, "Value could not be converted "
"to a double for multipleOf check");
}
return false;
}
i = static_cast<int64_t>(d);
} else {
return true;
}
if (i == 0) {
return true;
}
if (i % constraint.multipleOf != 0) {
if (results) {
results->pushError(context, "Value should be a multiple of " +
boost::lexical_cast<std::string>(constraint.multipleOf));
}
return false;
}
return true;
}

View File

@ -284,6 +284,11 @@ TEST_F(TestValidator, Draft4_MinProperties)
processDraft4TestFile(TEST_SUITE_DIR "draft4/minProperties.json");
}
TEST_F(TestValidator, Draft4_MultipleOf)
{
processDraft4TestFile(TEST_SUITE_DIR "draft4/multipleOf.json");
}
TEST_F(TestValidator, Draft4_Not)
{
processDraft4TestFile(TEST_SUITE_DIR "draft4/not.json");