Split MultipleOfConstraint into separate classes for integers and doubles, with improved encapsulation

This commit is contained in:
Tristan Penman 2016-01-12 08:21:03 +11:00
parent 14e31be16d
commit 7aa0ccb468
4 changed files with 183 additions and 101 deletions

View File

@ -598,17 +598,61 @@ struct MinPropertiesConstraint: BasicConstraint<MinPropertiesConstraint>
};
/**
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint
* @brief Represents either 'multipleOf' or 'divisibleBy' constraints where
* the divisor is a floating point number
*/
struct MultipleOfConstraint: BasicConstraint<MultipleOfConstraint>
class MultipleOfDoubleConstraint:
public BasicConstraint<MultipleOfDoubleConstraint>
{
explicit MultipleOfConstraint(int64_t value)
: value(value) { }
public:
MultipleOfDoubleConstraint()
: value(1.) { }
explicit MultipleOfConstraint(double value)
: value(value) { }
MultipleOfDoubleConstraint(CustomAlloc allocFn, CustomFree freeFn)
: BasicConstraint(allocFn, freeFn),
value(1.) { }
const boost::variant<double, int64_t> value;
double getDivisor() const
{
return value;
}
void setDivisor(double newValue)
{
value = newValue;
}
private:
double value;
};
/**
* @brief Represents either 'multipleOf' or 'divisibleBy' constraints where
* the divisor is of integer type
*/
class MultipleOfIntConstraint:
public BasicConstraint<MultipleOfIntConstraint>
{
public:
MultipleOfIntConstraint()
: value(1) { }
MultipleOfIntConstraint(CustomAlloc allocFn, CustomFree freeFn)
: BasicConstraint(allocFn, freeFn),
value(1) { }
int64_t getDivisor() const
{
return value;
}
void setDivisor(int64_t newValue)
{
value = newValue;
}
private:
int64_t value;
};
/**

View File

@ -8,7 +8,6 @@ namespace constraints {
struct FormatConstraint;
struct MaxPropertiesConstraint;
struct MinPropertiesConstraint;
struct MultipleOfConstraint;
class AllOfConstraint;
class AnyOfConstraint;
@ -21,6 +20,8 @@ class MaxLengthConstraint;
class MinItemsConstraint;
class MinimumConstraint;
class MinLengthConstraint;
class MultipleOfDoubleConstraint;
class MultipleOfIntConstraint;
class NotConstraint;
class OneOfConstraint;
class PatternConstraint;
@ -49,7 +50,8 @@ protected:
typedef constraints::MinItemsConstraint MinItemsConstraint;
typedef constraints::MinLengthConstraint MinLengthConstraint;
typedef constraints::MinPropertiesConstraint MinPropertiesConstraint;
typedef constraints::MultipleOfConstraint MultipleOfConstraint;
typedef constraints::MultipleOfDoubleConstraint MultipleOfDoubleConstraint;
typedef constraints::MultipleOfIntConstraint MultipleOfIntConstraint;
typedef constraints::NotConstraint NotConstraint;
typedef constraints::OneOfConstraint OneOfConstraint;
typedef constraints::PatternConstraint PatternConstraint;
@ -74,7 +76,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 MultipleOfDoubleConstraint &) = 0;
virtual bool visit(const MultipleOfIntConstraint &) = 0;
virtual bool visit(const NotConstraint &) = 0;
virtual bool visit(const OneOfConstraint &) = 0;
virtual bool visit(const PatternConstraint &) = 0;

View File

@ -186,10 +186,21 @@ private:
if ((itr = object.find("divisibleBy")) != object.end()) {
if (version == kDraft3) {
rootSchema.addConstraintToSubschema(
makeMultipleOfConstraint(itr->second), &subschema);
if (itr->second.maybeInteger()) {
rootSchema.addConstraintToSubschema(
makeMultipleOfIntConstraint(itr->second),
&subschema);
} else if (itr->second.maybeDouble()) {
rootSchema.addConstraintToSubschema(
makeMultipleOfDoubleConstraint(itr->second),
&subschema);
} else {
throw std::runtime_error("Expected an numeric value for "
" 'divisibleBy' constraint.");
}
} else {
throw std::runtime_error("'divisibleBy' constraint not valid after draft 3");
throw std::runtime_error(
"'divisibleBy' constraint not valid after draft 3");
}
}
@ -294,10 +305,19 @@ private:
if ((itr = object.find("multipleOf")) != object.end()) {
if (version == kDraft3) {
throw std::runtime_error("'multipleOf' constraint not available in draft 3");
} else {
throw std::runtime_error(
"'multipleOf' constraint not available in draft 3");
} else if (itr->second.maybeInteger()) {
rootSchema.addConstraintToSubschema(
makeMultipleOfConstraint(itr->second), &subschema);
makeMultipleOfIntConstraint(itr->second),
&subschema);
} else if (itr->second.maybeDouble()) {
rootSchema.addConstraintToSubschema(
makeMultipleOfDoubleConstraint(itr->second),
&subschema);
} else {
throw std::runtime_error("Expected an numeric value for "
" 'divisibleBy' constraint.");
}
}
@ -1112,27 +1132,37 @@ private:
}
/**
* @brief Make a new MultipleOfConstraint object.
* @brief Make a new MultipleOfDoubleConstraint object
*
* @param node JSON node containing an numeric value that a value must
* be divisible by.
* @param node JSON node containing an numeric value that a target value
* must divide by in order to satisfy this constraint
*
* @return pointer to a new MultipleOfConstraint that belongs to the
* caller
* @return a MultipleOfConstraint
*/
template<typename AdapterType>
constraints::MultipleOfConstraint makeMultipleOfConstraint(
constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(
const AdapterType &node)
{
// Allow both integral and double types to be provided
if (node.maybeInteger()) {
return constraints::MultipleOfConstraint(node.asInteger());
} else if (node.maybeDouble()) {
return constraints::MultipleOfConstraint(node.asDouble());
}
constraints::MultipleOfDoubleConstraint constraint;
constraint.setDivisor(node.asDouble());
return constraint;
}
throw std::runtime_error(
"Expected an numeric value for 'multipleOf' constraint.");
/**
* @brief Make a new MultipleOfIntConstraint object
*
* @param node JSON node containing a numeric value that a target value
* must divide by in order to satisfy this constraint
*
* @return a MultipleOfIntConstraint
*/
template<typename AdapterType>
constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(
const AdapterType &node)
{
constraints::MultipleOfIntConstraint constraint;
constraint.setDivisor(node.asInteger());
return constraint;
}
/**

View File

@ -590,100 +590,105 @@ public:
}
/**
* @brief Validate against the multipleOf or divisibleBy constraints
* represented by a MultipleOfConstraint object.
* @brief Validate a value against a MultipleOfDoubleConstraint
*
* @param constraint Constraint that the target must validate against.
* @param constraint Constraint that the target must validate against
*
* @return true if the constraint is satisfied, false otherwise.
* @return \c true if the constraint is satisfied; \c false otherwise
*/
virtual bool visit(const MultipleOfConstraint &constraint)
virtual bool visit(const MultipleOfDoubleConstraint &constraint)
{
const int64_t *multipleOfInteger = boost::get<int64_t>(&constraint.value);
if (multipleOfInteger) {
const double divisor = constraint.getDivisor();
double d = 0.;
if (target.maybeDouble()) {
if (!target.asDouble(d)) {
if (results) {
results->pushError(context, "Value could not be converted "
"to a number to check if it is a multiple of " +
boost::lexical_cast<std::string>(divisor));
}
return false;
}
} else if (target.maybeInteger()) {
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 % *multipleOfInteger != 0) {
if (!target.asInteger(i)) {
if (results) {
results->pushError(context, "Value should be a multiple of " +
boost::lexical_cast<std::string>(*multipleOfInteger));
results->pushError(context, "Value could not be converted "
"to a number to check if it is a multiple of " +
boost::lexical_cast<std::string>(divisor));
}
return false;
}
d = static_cast<double>(i);
} else {
return true;
}
const double *multipleOfDouble = boost::get<double>(&constraint.value);
if (multipleOfDouble) {
double d = 0.;
if (target.maybeDouble()) {
if (!target.asDouble(d)) {
if (results) {
results->pushError(context, "Value could not be converted "
"to a number to check if it is a multiple of " +
boost::lexical_cast<std::string>(*multipleOfDouble));
}
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 to check if it is a multiple of " +
boost::lexical_cast<std::string>(*multipleOfDouble));
}
return false;
}
d = static_cast<double>(i);
} else {
return true;
if (d == 0) {
return true;
}
const double r = remainder(d, divisor);
if (fabs(r) > std::numeric_limits<double>::epsilon()) {
if (results) {
results->pushError(context, "Value should be a multiple of " +
boost::lexical_cast<std::string>(divisor));
}
return false;
}
if (d == 0) {
return true;
}
return true;
}
const double r = remainder(d, *multipleOfDouble);
/**
* @brief Validate a value against a MultipleOfIntConstraint
*
* @param constraint Constraint that the target must validate against
*
* @return \c true if the constraint is satisfied; \c false otherwise
*/
virtual bool visit(const MultipleOfIntConstraint &constraint)
{
const int64_t divisor = constraint.getDivisor();
if (fabs(r) > std::numeric_limits<double>::epsilon()) {
int64_t i = 0;
if (target.maybeInteger()) {
if (!target.asInteger(i)) {
if (results) {
results->pushError(context, "Value should be a multiple of " +
boost::lexical_cast<std::string>(*multipleOfDouble));
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;
}
return false;
if (i == 0) {
return true;
}
if (i % divisor != 0) {
if (results) {
results->pushError(context, "Value should be a multiple of " +
boost::lexical_cast<std::string>(divisor));
}
return false;
}
return true;
}
/**