mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-09-27 16:29:33 +02:00
Split MultipleOfConstraint into separate classes for integers and doubles, with improved encapsulation
This commit is contained in:
parent
14e31be16d
commit
7aa0ccb468
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user