mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-09-27 16:29:33 +02:00
Implement multipleOf constraint using std::remainder
This commit is contained in:
parent
27c065b37f
commit
b377609efa
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user