mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-10-17 03:03:23 +02:00
Refactor memory management for schemas and constraints to improve API and allow for future improvements
This commit is contained in:
@@ -74,6 +74,7 @@ using std::endl;
|
|||||||
|
|
||||||
using valijson::Schema;
|
using valijson::Schema;
|
||||||
using valijson::SchemaParser;
|
using valijson::SchemaParser;
|
||||||
|
using valijson::Subschema;
|
||||||
using valijson::Validator;
|
using valijson::Validator;
|
||||||
using valijson::ValidationResults;
|
using valijson::ValidationResults;
|
||||||
using valijson::adapters::RapidJsonAdapter;
|
using valijson::adapters::RapidJsonAdapter;
|
||||||
@@ -90,49 +91,64 @@ void addPropertiesConstraint(Schema &schema)
|
|||||||
{
|
{
|
||||||
|
|
||||||
PropertiesConstraint::PropertySchemaMap propertySchemaMap;
|
PropertiesConstraint::PropertySchemaMap propertySchemaMap;
|
||||||
PropertiesConstraint::PropertySchemaMap patternPropertiesSchemaMap;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Create a child schema for the 'category' property that requires one
|
// Prepare an enum constraint requires a document to be equal to at
|
||||||
// of several possible values.
|
// least one of a set of possible values
|
||||||
Schema &propertySchema = propertySchemaMap["category"];
|
|
||||||
EnumConstraint::Values enumConstraintValues;
|
EnumConstraint::Values enumConstraintValues;
|
||||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("album"));
|
enumConstraintValues.push_back(new RapidJsonFrozenValue("album"));
|
||||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("book"));
|
enumConstraintValues.push_back(new RapidJsonFrozenValue("book"));
|
||||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("other"));
|
enumConstraintValues.push_back(new RapidJsonFrozenValue("other"));
|
||||||
enumConstraintValues.push_back(new RapidJsonFrozenValue("video"));
|
enumConstraintValues.push_back(new RapidJsonFrozenValue("video"));
|
||||||
propertySchema.addConstraint(new EnumConstraint(enumConstraintValues));
|
|
||||||
|
// Create a subschema, owned by the root schema, with a constraint
|
||||||
|
const Subschema *subschema = schema.createSubschema();
|
||||||
|
schema.addConstraintToSubschema(EnumConstraint(enumConstraintValues),
|
||||||
|
subschema);
|
||||||
|
|
||||||
|
// Include subschema in properties constraint
|
||||||
|
propertySchemaMap["category"] = subschema;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Create a child schema for the 'description' property that requires
|
// Create a child schema for the 'description' property that requires
|
||||||
// a string, but does not enforce any length constraints.
|
// a string, but does not enforce any length constraints.
|
||||||
Schema &propertySchema = propertySchemaMap["description"];
|
const Subschema *subschema = schema.createSubschema();
|
||||||
propertySchema.addConstraint(new TypeConstraint(TypeConstraint::kString));
|
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kString),
|
||||||
|
subschema);
|
||||||
|
|
||||||
|
// Include subschema in properties constraint
|
||||||
|
propertySchemaMap["description"] = subschema;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Create a child schema for the 'price' property, that requires a
|
// Create a child schema for the 'price' property, that requires a
|
||||||
// number with a value greater than zero.
|
// number with a value greater than zero.
|
||||||
Schema &propertySchema = propertySchemaMap["price"];
|
const Subschema *subschema = schema.createSubschema();
|
||||||
propertySchema.addConstraint(new MinimumConstraint(0.0, true));
|
schema.addConstraintToSubschema(MinimumConstraint(0.0, true), subschema);
|
||||||
propertySchema.addConstraint(new TypeConstraint(TypeConstraint::kNumber));
|
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kNumber),
|
||||||
|
subschema);
|
||||||
|
|
||||||
|
// Include subschema in properties constraint
|
||||||
|
propertySchemaMap["price"] = subschema;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Create a child schema for the 'title' property that requires a string
|
// Create a child schema for the 'title' property that requires a string
|
||||||
// that is between 1 and 200 characters in length.
|
// that is between 1 and 200 characters in length.
|
||||||
Schema &propertySchema = propertySchemaMap["title"];
|
const Subschema *subschema = schema.createSubschema();
|
||||||
propertySchema.addConstraint(new MaxLengthConstraint(200));
|
schema.addConstraintToSubschema(MaxLengthConstraint(200), subschema);
|
||||||
propertySchema.addConstraint(new MinLengthConstraint(1));
|
schema.addConstraintToSubschema(MinLengthConstraint(1), subschema);
|
||||||
propertySchema.addConstraint(new TypeConstraint(TypeConstraint::kString));
|
schema.addConstraintToSubschema(TypeConstraint(TypeConstraint::kString),
|
||||||
|
subschema);
|
||||||
|
|
||||||
|
// Include subschema in properties constraint
|
||||||
|
propertySchemaMap["title"] = subschema;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a PropertiesConstraint to the schema, with the properties defined
|
// Add a PropertiesConstraint to the schema, with the properties defined
|
||||||
// above, no pattern properties, and with additional property schemas
|
// above, no pattern properties or additional property schemas
|
||||||
// prohibited.
|
schema.addConstraint(PropertiesConstraint(propertySchemaMap));
|
||||||
schema.addConstraint(new PropertiesConstraint(
|
|
||||||
propertySchemaMap, patternPropertiesSchemaMap));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addRequiredConstraint(Schema &schema)
|
void addRequiredConstraint(Schema &schema)
|
||||||
@@ -176,10 +192,10 @@ int main(int argc, char *argv[])
|
|||||||
addTypeConstraint(schema);
|
addTypeConstraint(schema);
|
||||||
|
|
||||||
// Perform validation
|
// Perform validation
|
||||||
Validator validator(schema);
|
Validator validator;
|
||||||
ValidationResults results;
|
ValidationResults results;
|
||||||
RapidJsonAdapter targetDocumentAdapter(targetDocument);
|
RapidJsonAdapter targetDocumentAdapter(targetDocument);
|
||||||
if (!validator.validate(targetDocumentAdapter, &results)) {
|
if (!validator.validate(schema, targetDocumentAdapter, &results)) {
|
||||||
std::cerr << "Validation failed." << endl;
|
std::cerr << "Validation failed." << endl;
|
||||||
ValidationResults::Error error;
|
ValidationResults::Error error;
|
||||||
unsigned int errorNum = 1;
|
unsigned int errorNum = 1;
|
||||||
|
@@ -60,11 +60,10 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform validation
|
// Perform validation
|
||||||
Validator validator(schema);
|
Validator validator(Validator::kWeakTypes);
|
||||||
validator.setStrict(false);
|
|
||||||
ValidationResults results;
|
ValidationResults results;
|
||||||
RapidJsonAdapter targetDocumentAdapter(targetDocument);
|
RapidJsonAdapter targetDocumentAdapter(targetDocument);
|
||||||
if (!validator.validate(targetDocumentAdapter, &results)) {
|
if (!validator.validate(schema, targetDocumentAdapter, &results)) {
|
||||||
std::cerr << "Validation failed." << endl;
|
std::cerr << "Validation failed." << endl;
|
||||||
ValidationResults::Error error;
|
ValidationResults::Error error;
|
||||||
unsigned int errorNum = 1;
|
unsigned int errorNum = 1;
|
||||||
|
@@ -16,14 +16,14 @@
|
|||||||
#ifndef __VALIJSON_CONSTRAINTS_CONCRETE_CONSTRAINTS_HPP
|
#ifndef __VALIJSON_CONSTRAINTS_CONCRETE_CONSTRAINTS_HPP
|
||||||
#define __VALIJSON_CONSTRAINTS_CONCRETE_CONSTRAINTS_HPP
|
#define __VALIJSON_CONSTRAINTS_CONCRETE_CONSTRAINTS_HPP
|
||||||
|
|
||||||
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <boost/ptr_container/ptr_map.hpp>
|
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
|
||||||
#include <boost/shared_ptr.hpp>
|
|
||||||
|
|
||||||
#include <valijson/adapters/frozen_value.hpp>
|
#include <valijson/adapters/frozen_value.hpp>
|
||||||
#include <valijson/constraints/basic_constraint.hpp>
|
#include <valijson/constraints/basic_constraint.hpp>
|
||||||
@@ -44,7 +44,7 @@ namespace constraints {
|
|||||||
*/
|
*/
|
||||||
struct AllOfConstraint: BasicConstraint<AllOfConstraint>
|
struct AllOfConstraint: BasicConstraint<AllOfConstraint>
|
||||||
{
|
{
|
||||||
typedef boost::ptr_vector<Schema> Schemas;
|
typedef std::vector<const Subschema *> Schemas;
|
||||||
|
|
||||||
AllOfConstraint(const Schemas &schemas)
|
AllOfConstraint(const Schemas &schemas)
|
||||||
: schemas(schemas) { }
|
: schemas(schemas) { }
|
||||||
@@ -62,7 +62,7 @@ struct AllOfConstraint: BasicConstraint<AllOfConstraint>
|
|||||||
*/
|
*/
|
||||||
struct AnyOfConstraint: BasicConstraint<AnyOfConstraint>
|
struct AnyOfConstraint: BasicConstraint<AnyOfConstraint>
|
||||||
{
|
{
|
||||||
typedef boost::ptr_vector<Schema> Schemas;
|
typedef std::vector<const Subschema *> Schemas;
|
||||||
|
|
||||||
AnyOfConstraint(const Schemas &schemas)
|
AnyOfConstraint(const Schemas &schemas)
|
||||||
: schemas(schemas) { }
|
: schemas(schemas) { }
|
||||||
@@ -84,7 +84,8 @@ struct DependenciesConstraint: BasicConstraint<DependenciesConstraint>
|
|||||||
typedef std::map<std::string, Dependencies> PropertyDependenciesMap;
|
typedef std::map<std::string, Dependencies> PropertyDependenciesMap;
|
||||||
|
|
||||||
// A mapping from property names to dependent schemas
|
// A mapping from property names to dependent schemas
|
||||||
typedef boost::ptr_map<std::string, Schema> PropertyDependentSchemasMap;
|
typedef std::map<std::string, const Subschema *>
|
||||||
|
PropertyDependentSchemasMap;
|
||||||
|
|
||||||
DependenciesConstraint(const PropertyDependenciesMap &dependencies,
|
DependenciesConstraint(const PropertyDependenciesMap &dependencies,
|
||||||
const PropertyDependentSchemasMap &dependentSchemas)
|
const PropertyDependentSchemasMap &dependentSchemas)
|
||||||
@@ -117,7 +118,7 @@ struct EnumConstraint: BasicConstraint<EnumConstraint>
|
|||||||
*/
|
*/
|
||||||
struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
||||||
{
|
{
|
||||||
typedef boost::ptr_vector<Schema> Schemas;
|
typedef std::vector<const Subschema *> Schemas;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a singular item constraint that allows no additional
|
* @brief Construct a singular item constraint that allows no additional
|
||||||
@@ -125,8 +126,9 @@ struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
|||||||
*
|
*
|
||||||
* @param itemSchema
|
* @param itemSchema
|
||||||
*/
|
*/
|
||||||
ItemsConstraint(const Schema &itemSchema)
|
ItemsConstraint(const Subschema *itemSchema)
|
||||||
: itemSchema(new Schema(itemSchema)) { }
|
: itemSchema(itemSchema),
|
||||||
|
additionalItemsSchema(NULL) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a singular item schema that allows additional items
|
* @brief Construct a singular item schema that allows additional items
|
||||||
@@ -134,10 +136,10 @@ struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
|||||||
* @param itemSchema
|
* @param itemSchema
|
||||||
* @param additionalItemsSchema
|
* @param additionalItemsSchema
|
||||||
*/
|
*/
|
||||||
ItemsConstraint(const Schema &itemSchema,
|
ItemsConstraint(const Subschema *itemSchema,
|
||||||
const Schema &additionalItemsSchema)
|
const Subschema *additionalItemsSchema)
|
||||||
: itemSchema(new Schema(itemSchema)),
|
: itemSchema(itemSchema),
|
||||||
additionalItemsSchema(new Schema(additionalItemsSchema)) { }
|
additionalItemsSchema(additionalItemsSchema) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a plural items constraint that does not allow for
|
* @brief Construct a plural items constraint that does not allow for
|
||||||
@@ -146,7 +148,9 @@ struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
|||||||
* @param itemSchemas collection of item schemas
|
* @param itemSchemas collection of item schemas
|
||||||
*/
|
*/
|
||||||
ItemsConstraint(const Schemas &itemSchemas)
|
ItemsConstraint(const Schemas &itemSchemas)
|
||||||
: itemSchemas(new Schemas(itemSchemas)) { }
|
: itemSchema(NULL),
|
||||||
|
itemSchemas(itemSchemas),
|
||||||
|
additionalItemsSchema(NULL) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a plural items constraint that allows additional items
|
* @brief Construct a plural items constraint that allows additional items
|
||||||
@@ -155,21 +159,22 @@ struct ItemsConstraint: BasicConstraint<ItemsConstraint>
|
|||||||
* @param additionalItemsSchema
|
* @param additionalItemsSchema
|
||||||
*/
|
*/
|
||||||
ItemsConstraint(const Schemas &itemSchemas,
|
ItemsConstraint(const Schemas &itemSchemas,
|
||||||
const Schema &additionalItemsSchema)
|
const Subschema *additionalItemsSchema)
|
||||||
: itemSchemas(new Schemas(itemSchemas)),
|
: itemSchema(NULL),
|
||||||
additionalItemsSchema(new Schema(additionalItemsSchema)) { }
|
itemSchemas(itemSchemas),
|
||||||
|
additionalItemsSchema(additionalItemsSchema) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy constructor
|
* @brief Copy constructor
|
||||||
*/
|
*/
|
||||||
ItemsConstraint(const ItemsConstraint &other)
|
ItemsConstraint(const ItemsConstraint &other)
|
||||||
: itemSchema(other.itemSchema ? new Schema(*other.itemSchema.get()) : NULL),
|
: itemSchema(other.itemSchema),
|
||||||
itemSchemas(other.itemSchemas ? new Schemas(*other.itemSchemas.get()) : NULL),
|
itemSchemas(other.itemSchemas),
|
||||||
additionalItemsSchema(other.additionalItemsSchema ? new Schema(*other.additionalItemsSchema.get()) : NULL) { }
|
additionalItemsSchema(other.additionalItemsSchema) { }
|
||||||
|
|
||||||
const boost::scoped_ptr<const Schema> itemSchema;
|
const Subschema* itemSchema;
|
||||||
const boost::scoped_ptr<const Schemas> itemSchemas;
|
const Schemas itemSchemas;
|
||||||
const boost::scoped_ptr<const Schema> additionalItemsSchema;
|
const Subschema* additionalItemsSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,25 +270,17 @@ struct MinPropertiesConstraint: BasicConstraint<MinPropertiesConstraint>
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint for decimals
|
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint
|
||||||
*/
|
*/
|
||||||
struct MultipleOfDecimalConstraint: BasicConstraint<MultipleOfDecimalConstraint>
|
struct MultipleOfConstraint: BasicConstraint<MultipleOfConstraint>
|
||||||
{
|
{
|
||||||
MultipleOfDecimalConstraint(double multipleOf)
|
explicit MultipleOfConstraint(int64_t value)
|
||||||
: multipleOf(multipleOf) { }
|
: value(value) { }
|
||||||
|
|
||||||
const double multipleOf;
|
explicit MultipleOfConstraint(double value)
|
||||||
};
|
: value(value) { }
|
||||||
|
|
||||||
/**
|
const boost::variant<double, int64_t> value;
|
||||||
* @brief Represents a 'multipleOf' or 'divisibleBy' constraint for int64_t
|
|
||||||
*/
|
|
||||||
struct MultipleOfIntegerConstraint: BasicConstraint<MultipleOfIntegerConstraint>
|
|
||||||
{
|
|
||||||
MultipleOfIntegerConstraint(int64_t multipleOf)
|
|
||||||
: multipleOf(multipleOf) { }
|
|
||||||
|
|
||||||
const int64_t multipleOf;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,13 +288,13 @@ struct MultipleOfIntegerConstraint: BasicConstraint<MultipleOfIntegerConstraint>
|
|||||||
*/
|
*/
|
||||||
struct NotConstraint: BasicConstraint<NotConstraint>
|
struct NotConstraint: BasicConstraint<NotConstraint>
|
||||||
{
|
{
|
||||||
NotConstraint(const Schema &schema)
|
NotConstraint(const Subschema *schema)
|
||||||
: schema(new Schema(schema)) { }
|
: schema(schema) { }
|
||||||
|
|
||||||
NotConstraint(const NotConstraint &other)
|
NotConstraint(const NotConstraint &other)
|
||||||
: schema(other.schema ? new Schema(*other.schema) : NULL) { }
|
: schema(other.schema) { }
|
||||||
|
|
||||||
const boost::scoped_ptr<const Schema> schema;
|
const Subschema *schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,7 +302,7 @@ struct NotConstraint: BasicConstraint<NotConstraint>
|
|||||||
*/
|
*/
|
||||||
struct OneOfConstraint: BasicConstraint<OneOfConstraint>
|
struct OneOfConstraint: BasicConstraint<OneOfConstraint>
|
||||||
{
|
{
|
||||||
typedef boost::ptr_vector<Schema> Schemas;
|
typedef std::vector<const Subschema *> Schemas;
|
||||||
|
|
||||||
OneOfConstraint(const Schemas &schemas)
|
OneOfConstraint(const Schemas &schemas)
|
||||||
: schemas(schemas) { }
|
: schemas(schemas) { }
|
||||||
@@ -331,29 +328,33 @@ struct PatternConstraint: BasicConstraint<PatternConstraint>
|
|||||||
*/
|
*/
|
||||||
struct PropertiesConstraint: BasicConstraint<PropertiesConstraint> {
|
struct PropertiesConstraint: BasicConstraint<PropertiesConstraint> {
|
||||||
|
|
||||||
typedef boost::ptr_map<std::string, Schema> PropertySchemaMap;
|
typedef std::map<std::string, const Subschema *> PropertySchemaMap;
|
||||||
|
|
||||||
|
PropertiesConstraint(const PropertySchemaMap &properties)
|
||||||
|
: properties(properties),
|
||||||
|
additionalProperties(NULL) { }
|
||||||
|
|
||||||
PropertiesConstraint(const PropertySchemaMap &properties,
|
PropertiesConstraint(const PropertySchemaMap &properties,
|
||||||
const PropertySchemaMap &patternProperties)
|
const PropertySchemaMap &patternProperties)
|
||||||
: properties(properties),
|
: properties(properties),
|
||||||
patternProperties(patternProperties) { }
|
patternProperties(patternProperties),
|
||||||
|
additionalProperties(NULL) { }
|
||||||
|
|
||||||
PropertiesConstraint(const PropertySchemaMap &properties,
|
PropertiesConstraint(const PropertySchemaMap &properties,
|
||||||
const PropertySchemaMap &patternProperties,
|
const PropertySchemaMap &patternProperties,
|
||||||
const Schema &additionalProperties)
|
const Subschema *additionalProperties)
|
||||||
: properties(properties),
|
: properties(properties),
|
||||||
patternProperties(patternProperties),
|
patternProperties(patternProperties),
|
||||||
additionalProperties(new Schema(additionalProperties)) { }
|
additionalProperties(additionalProperties) { }
|
||||||
|
|
||||||
PropertiesConstraint(const PropertiesConstraint &other)
|
PropertiesConstraint(const PropertiesConstraint &other)
|
||||||
: properties(other.properties),
|
: properties(other.properties),
|
||||||
patternProperties(other.patternProperties),
|
patternProperties(other.patternProperties),
|
||||||
additionalProperties(other.additionalProperties ?
|
additionalProperties(other.additionalProperties) {}
|
||||||
new Schema(*other.additionalProperties.get()) : NULL) {}
|
|
||||||
|
|
||||||
const PropertySchemaMap properties;
|
const PropertySchemaMap properties;
|
||||||
const PropertySchemaMap patternProperties;
|
const PropertySchemaMap patternProperties;
|
||||||
const boost::scoped_ptr<const Schema> additionalProperties;
|
const Subschema *additionalProperties;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -391,15 +392,15 @@ struct TypeConstraint: BasicConstraint<TypeConstraint>
|
|||||||
|
|
||||||
typedef std::set<JsonType> JsonTypes;
|
typedef std::set<JsonType> JsonTypes;
|
||||||
|
|
||||||
typedef boost::ptr_vector<Schema> Schemas;
|
typedef std::vector<const Subschema *> Schemas;
|
||||||
|
|
||||||
TypeConstraint(const JsonType jsonType)
|
TypeConstraint(const JsonType jsonType)
|
||||||
: jsonTypes(makeJsonTypes(jsonType)) { }
|
: jsonTypes(makeJsonTypes(jsonType)) { }
|
||||||
|
|
||||||
TypeConstraint(const JsonTypes jsonTypes)
|
TypeConstraint(const JsonTypes &jsonTypes)
|
||||||
: jsonTypes(jsonTypes) { }
|
: jsonTypes(jsonTypes) { }
|
||||||
|
|
||||||
TypeConstraint(const JsonTypes jsonTypes,
|
TypeConstraint(const JsonTypes &jsonTypes,
|
||||||
const Schemas &schemas)
|
const Schemas &schemas)
|
||||||
: jsonTypes(jsonTypes),
|
: jsonTypes(jsonTypes),
|
||||||
schemas(schemas) { }
|
schemas(schemas) { }
|
||||||
|
@@ -19,8 +19,7 @@ struct MinimumConstraint;
|
|||||||
struct MinItemsConstraint;
|
struct MinItemsConstraint;
|
||||||
struct MinLengthConstraint;
|
struct MinLengthConstraint;
|
||||||
struct MinPropertiesConstraint;
|
struct MinPropertiesConstraint;
|
||||||
struct MultipleOfDecimalConstraint;
|
struct MultipleOfConstraint;
|
||||||
struct MultipleOfIntegerConstraint;
|
|
||||||
struct NotConstraint;
|
struct NotConstraint;
|
||||||
struct OneOfConstraint;
|
struct OneOfConstraint;
|
||||||
struct PatternConstraint;
|
struct PatternConstraint;
|
||||||
@@ -48,8 +47,7 @@ protected:
|
|||||||
typedef constraints::MinItemsConstraint MinItemsConstraint;
|
typedef constraints::MinItemsConstraint MinItemsConstraint;
|
||||||
typedef constraints::MinLengthConstraint MinLengthConstraint;
|
typedef constraints::MinLengthConstraint MinLengthConstraint;
|
||||||
typedef constraints::MinPropertiesConstraint MinPropertiesConstraint;
|
typedef constraints::MinPropertiesConstraint MinPropertiesConstraint;
|
||||||
typedef constraints::MultipleOfDecimalConstraint MultipleOfDecimalConstraint;
|
typedef constraints::MultipleOfConstraint MultipleOfConstraint;
|
||||||
typedef constraints::MultipleOfIntegerConstraint MultipleOfIntegerConstraint;
|
|
||||||
typedef constraints::NotConstraint NotConstraint;
|
typedef constraints::NotConstraint NotConstraint;
|
||||||
typedef constraints::OneOfConstraint OneOfConstraint;
|
typedef constraints::OneOfConstraint OneOfConstraint;
|
||||||
typedef constraints::PatternConstraint PatternConstraint;
|
typedef constraints::PatternConstraint PatternConstraint;
|
||||||
@@ -73,8 +71,7 @@ public:
|
|||||||
virtual bool visit(const MinItemsConstraint &) = 0;
|
virtual bool visit(const MinItemsConstraint &) = 0;
|
||||||
virtual bool visit(const MinLengthConstraint &) = 0;
|
virtual bool visit(const MinLengthConstraint &) = 0;
|
||||||
virtual bool visit(const MinPropertiesConstraint &) = 0;
|
virtual bool visit(const MinPropertiesConstraint &) = 0;
|
||||||
virtual bool visit(const MultipleOfDecimalConstraint &) = 0;
|
virtual bool visit(const MultipleOfConstraint &) = 0;
|
||||||
virtual bool visit(const MultipleOfIntegerConstraint &) = 0;
|
|
||||||
virtual bool visit(const NotConstraint &) = 0;
|
virtual bool visit(const NotConstraint &) = 0;
|
||||||
virtual bool visit(const OneOfConstraint &) = 0;
|
virtual bool visit(const OneOfConstraint &) = 0;
|
||||||
virtual bool visit(const PatternConstraint &) = 0;
|
virtual bool visit(const PatternConstraint &) = 0;
|
||||||
|
@@ -21,7 +21,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Construct a new Schema instance with no constraints
|
* @brief Construct a new Schema instance with no constraints
|
||||||
*/
|
*/
|
||||||
Schema() {}
|
Schema()
|
||||||
|
: sharedEmptySubschema(newSubschema()) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clean up and free all memory managed by the Schema
|
* @brief Clean up and free all memory managed by the Schema
|
||||||
@@ -31,11 +32,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual ~Schema()
|
virtual ~Schema()
|
||||||
{
|
{
|
||||||
|
sharedEmptySubschema->~Subschema();
|
||||||
|
::operator delete((void*)(sharedEmptySubschema));
|
||||||
|
sharedEmptySubschema = NULL;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!subschemaSet.empty()) {
|
for (std::set<Subschema *>::iterator itr = subschemaSet.begin();
|
||||||
std::set<Subschema*>::iterator itr = subschemaSet.begin();
|
itr != subschemaSet.end(); ++itr) {
|
||||||
Subschema *subschema = *itr;
|
Subschema *subschema = *itr;
|
||||||
subschemaSet.erase(itr);
|
|
||||||
subschema->~Subschema();
|
subschema->~Subschema();
|
||||||
// TODO: Replace with custom free function
|
// TODO: Replace with custom free function
|
||||||
::operator delete(subschema);
|
::operator delete(subschema);
|
||||||
@@ -60,20 +64,9 @@ public:
|
|||||||
void addConstraintToSubschema(const Constraint &constraint,
|
void addConstraintToSubschema(const Constraint &constraint,
|
||||||
const Subschema *subschema)
|
const Subschema *subschema)
|
||||||
{
|
{
|
||||||
if (subschema == this) {
|
|
||||||
addConstraint(constraint);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Subschema *noConst = const_cast<Subschema*>(subschema);
|
|
||||||
if (subschemaSet.find(noConst) == subschemaSet.end()) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Subschema pointer is not owned by this Schema instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check heirarchy for subschemas that do not belong...
|
// TODO: Check heirarchy for subschemas that do not belong...
|
||||||
|
|
||||||
noConst->addConstraint(constraint);
|
mutableSubschema(subschema)->addConstraint(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,25 +76,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
const Subschema * createSubschema()
|
const Subschema * createSubschema()
|
||||||
{
|
{
|
||||||
// TODO: Replace with custom malloc function
|
Subschema *subschema = newSubschema();
|
||||||
void *ptr = ::operator new(sizeof(Subschema));
|
|
||||||
if (!ptr) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Failed to allocate memory for sub-schema");
|
|
||||||
}
|
|
||||||
|
|
||||||
Subschema *subschema = NULL;
|
|
||||||
try {
|
|
||||||
subschema = new (ptr) Subschema();
|
|
||||||
if (!subschema) {
|
|
||||||
throw std::runtime_error("Failed to construct sub-schema");
|
|
||||||
}
|
|
||||||
ptr = NULL;
|
|
||||||
} catch (...) {
|
|
||||||
// TODO: Replace with custom free function
|
|
||||||
::operator delete(ptr);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!subschemaSet.insert(subschema).second) {
|
if (!subschemaSet.insert(subschema).second) {
|
||||||
@@ -111,13 +86,21 @@ public:
|
|||||||
} catch (...) {
|
} catch (...) {
|
||||||
subschema->~Subschema();
|
subschema->~Subschema();
|
||||||
// TODO: Replace with custom free function
|
// TODO: Replace with custom free function
|
||||||
::operator delete(ptr);
|
::operator delete(subschema);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return subschema;
|
return subschema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the shared empty schema
|
||||||
|
*/
|
||||||
|
const Subschema * emptySubschema() const
|
||||||
|
{
|
||||||
|
return sharedEmptySubschema;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a pointer to the root sub-schema of this Schema instance
|
* @brief Get a pointer to the root sub-schema of this Schema instance
|
||||||
*/
|
*/
|
||||||
@@ -126,10 +109,102 @@ public:
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the description for one of the sub-schemas owned by this
|
||||||
|
* Schema instance
|
||||||
|
*
|
||||||
|
* @param subschema sub-schema to update
|
||||||
|
* @param description new description
|
||||||
|
*/
|
||||||
|
void setSubschemaDescription(const Subschema *subschema,
|
||||||
|
const std::string &description)
|
||||||
|
{
|
||||||
|
mutableSubschema(subschema)->setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the ID for one of the sub-schemas owned by this Schema
|
||||||
|
* instance
|
||||||
|
*
|
||||||
|
* @param subschema sub-schema to update
|
||||||
|
* @param id new ID
|
||||||
|
*/
|
||||||
|
void setSubschemaId(const Subschema *subschema, const std::string &id)
|
||||||
|
{
|
||||||
|
mutableSubschema(subschema)->setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the title for one of the sub-schemas owned by this Schema
|
||||||
|
* instance
|
||||||
|
*
|
||||||
|
* @param subschema sub-schema to update
|
||||||
|
* @param title new title
|
||||||
|
*/
|
||||||
|
void setSubschemaTitle(const Subschema *subschema, const std::string &title)
|
||||||
|
{
|
||||||
|
mutableSubschema(subschema)->setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
// Disable copy construction
|
||||||
|
Schema(const Schema &);
|
||||||
|
|
||||||
|
// Disable copy assignment
|
||||||
|
Schema & operator=(const Schema &);
|
||||||
|
|
||||||
|
static Subschema *newSubschema()
|
||||||
|
{
|
||||||
|
// TODO: Replace with custom alloc function
|
||||||
|
void *ptr = ::operator new(sizeof(Subschema));
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to allocate memory for shared empty sub-schema");
|
||||||
|
}
|
||||||
|
|
||||||
|
Subschema *subschema = NULL;
|
||||||
|
try {
|
||||||
|
subschema = new (ptr) Subschema();
|
||||||
|
if (!subschema) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to construct shared empty sub-schema");
|
||||||
|
}
|
||||||
|
ptr = NULL;
|
||||||
|
} catch (...) {
|
||||||
|
// TODO: Replace with custom free function
|
||||||
|
::operator delete(ptr);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subschema;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subschema * mutableSubschema(const Subschema *subschema)
|
||||||
|
{
|
||||||
|
if (subschema == this) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subschema == sharedEmptySubschema) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Cannot modify the shared empty sub-schema");
|
||||||
|
}
|
||||||
|
|
||||||
|
Subschema *noConst = const_cast<Subschema*>(subschema);
|
||||||
|
if (subschemaSet.find(noConst) == subschemaSet.end()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Subschema pointer is not owned by this Schema instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
return noConst;
|
||||||
|
}
|
||||||
|
|
||||||
/// Set of Subschema instances owned by this schema
|
/// Set of Subschema instances owned by this schema
|
||||||
std::set<Subschema*> subschemaSet;
|
std::set<Subschema*> subschemaSet;
|
||||||
|
|
||||||
|
/// Empty schema that can be reused by multiple constraints
|
||||||
|
const Subschema *sharedEmptySubschema;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace valijson
|
} // namespace valijson
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,11 @@
|
|||||||
#ifndef __VALIJSON_SUBSCHEMA_HPP
|
#ifndef __VALIJSON_SUBSCHEMA_HPP
|
||||||
#define __VALIJSON_SUBSCHEMA_HPP
|
#define __VALIJSON_SUBSCHEMA_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
|
||||||
|
|
||||||
#include <valijson/constraints/constraint.hpp>
|
#include <valijson/constraints/constraint.hpp>
|
||||||
|
|
||||||
@@ -51,6 +52,24 @@ public:
|
|||||||
: constraints(subschema.constraints),
|
: constraints(subschema.constraints),
|
||||||
title(subschema.title) { }
|
title(subschema.title) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clean up and free all memory managed by the Subschema
|
||||||
|
*/
|
||||||
|
virtual ~Subschema()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
for (std::vector<const Constraint *>::iterator itr =
|
||||||
|
constraints.begin(); itr != constraints.end(); ++itr) {
|
||||||
|
const Constraint *constraint = *itr;
|
||||||
|
delete constraint;
|
||||||
|
}
|
||||||
|
constraints.clear();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
fprintf(stderr, "Caught an exception in Subschema destructor: %s",
|
||||||
|
e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add a constraint to this sub-schema
|
* @brief Add a constraint to this sub-schema
|
||||||
*
|
*
|
||||||
@@ -94,8 +113,8 @@ public:
|
|||||||
bool apply(ApplyFunction &applyFunction) const
|
bool apply(ApplyFunction &applyFunction) const
|
||||||
{
|
{
|
||||||
bool allTrue = true;
|
bool allTrue = true;
|
||||||
BOOST_FOREACH( const Constraint &constraint, constraints ) {
|
BOOST_FOREACH( const Constraint *constraint, constraints ) {
|
||||||
allTrue = allTrue && applyFunction(constraint);
|
allTrue = allTrue && applyFunction(*constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allTrue;
|
return allTrue;
|
||||||
@@ -113,8 +132,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool applyStrict(ApplyFunction &applyFunction) const
|
bool applyStrict(ApplyFunction &applyFunction) const
|
||||||
{
|
{
|
||||||
BOOST_FOREACH( const Constraint &constraint, constraints ) {
|
BOOST_FOREACH( const Constraint *constraint, constraints ) {
|
||||||
if (!applyFunction(constraint)) {
|
if (!applyFunction(*constraint)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +257,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
/// List of pointers to constraints that apply to this schema.
|
/// List of pointers to constraints that apply to this schema.
|
||||||
boost::ptr_vector<Constraint> constraints;
|
std::vector<const Constraint *> constraints;
|
||||||
|
|
||||||
/// Schema description (optional)
|
/// Schema description (optional)
|
||||||
boost::optional<std::string> description;
|
boost::optional<std::string> description;
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
#include <boost/variant/get.hpp>
|
||||||
|
|
||||||
#include <valijson/constraints/concrete_constraints.hpp>
|
#include <valijson/constraints/concrete_constraints.hpp>
|
||||||
#include <valijson/constraints/constraint_visitor.hpp>
|
#include <valijson/constraints/constraint_visitor.hpp>
|
||||||
@@ -64,18 +65,18 @@ public:
|
|||||||
*
|
*
|
||||||
* @return true if validation passes, false otherwise
|
* @return true if validation passes, false otherwise
|
||||||
*/
|
*/
|
||||||
bool validateSchema(const Schema &schema)
|
bool validateSchema(const Subschema &subschema)
|
||||||
{
|
{
|
||||||
// Wrap the validationCallback() function below so that it will be
|
// Wrap the validationCallback() function below so that it will be
|
||||||
// passed a reference to a constraint (_1), and a reference to the
|
// passed a reference to a constraint (_1), and a reference to the
|
||||||
// visitor (*this).
|
// visitor (*this).
|
||||||
Schema::ApplyFunction fn(boost::bind(validationCallback, _1, *this));
|
Subschema::ApplyFunction fn(boost::bind(validationCallback, _1, *this));
|
||||||
|
|
||||||
// Perform validation against each constraint defined in the schema
|
// Perform validation against each constraint defined in the schema
|
||||||
if (results == NULL) {
|
if (results == NULL) {
|
||||||
// The applyStrict() function will return immediately if the
|
// The applyStrict() function will return immediately if the
|
||||||
// callback function returns false
|
// callback function returns false
|
||||||
if (!schema.applyStrict(fn)) {
|
if (!subschema.applyStrict(fn)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -83,7 +84,7 @@ public:
|
|||||||
// schema, even if the callback function returns false. Once
|
// schema, even if the callback function returns false. Once
|
||||||
// iteration is complete, the apply() function will return true
|
// iteration is complete, the apply() function will return true
|
||||||
// only if all invokations of the callback function returned true.
|
// only if all invokations of the callback function returned true.
|
||||||
if (!schema.apply(fn)) {
|
if (!subschema.apply(fn)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,10 +117,10 @@ public:
|
|||||||
|
|
||||||
// Validate against each child schema
|
// Validate against each child schema
|
||||||
unsigned int index = 0;
|
unsigned int index = 0;
|
||||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
BOOST_FOREACH( const Subschema *subschema, constraint.schemas ) {
|
||||||
|
|
||||||
// Ensure that the target validates against child schema
|
// Ensure that the target validates against child schema
|
||||||
if (!validateSchema(schema)) {
|
if (!validateSchema(*subschema)) {
|
||||||
if (results) {
|
if (results) {
|
||||||
validated = false;
|
validated = false;
|
||||||
results->pushError(context,
|
results->pushError(context,
|
||||||
@@ -161,8 +162,8 @@ public:
|
|||||||
// visitor (*this).
|
// visitor (*this).
|
||||||
Schema::ApplyFunction fn(boost::bind(validationCallback, _1, *this));
|
Schema::ApplyFunction fn(boost::bind(validationCallback, _1, *this));
|
||||||
|
|
||||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
BOOST_FOREACH( const Subschema *subschema, constraint.schemas ) {
|
||||||
if (schema.apply(fn)) {
|
if (subschema->apply(fn)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,8 +232,8 @@ public:
|
|||||||
// dependent schema.
|
// dependent schema.
|
||||||
PDSM::const_iterator depSchemasItr = depSchemas.find(m.first);
|
PDSM::const_iterator depSchemasItr = depSchemas.find(m.first);
|
||||||
if (depSchemasItr != depSchemas.end()) {
|
if (depSchemasItr != depSchemas.end()) {
|
||||||
const Schema *schema = depSchemasItr->second;
|
const Subschema *subschema = depSchemasItr->second;
|
||||||
if (!validateSchema(*schema)) {
|
if (!validateSchema(*subschema)) {
|
||||||
if (results) {
|
if (results) {
|
||||||
results->pushError(context, "Failed to validate against dependent schema.");
|
results->pushError(context, "Failed to validate against dependent schema.");
|
||||||
validated = false;
|
validated = false;
|
||||||
@@ -326,14 +327,14 @@ public:
|
|||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (constraint.itemSchemas) {
|
} else if (!constraint.itemSchemas.empty()) {
|
||||||
|
|
||||||
// Get access to the target as an object
|
// Get access to the target as an object
|
||||||
const typename AdapterType::Array arr = target.asArray();
|
const typename AdapterType::Array arr = target.asArray();
|
||||||
|
|
||||||
if (!constraint.additionalItemsSchema) {
|
if (!constraint.additionalItemsSchema) {
|
||||||
// Check that the array length is <= length of the itemsSchema list
|
// Check that the array length is <= length of the itemsSchema list
|
||||||
if (arr.size() > constraint.itemSchemas->size()) {
|
if (arr.size() > constraint.itemSchemas.size()) {
|
||||||
if (results) {
|
if (results) {
|
||||||
results->pushError(context, "Array contains more items than allowed by items constraint.");
|
results->pushError(context, "Array contains more items than allowed by items constraint.");
|
||||||
validated = false;
|
validated = false;
|
||||||
@@ -351,7 +352,7 @@ public:
|
|||||||
newContext.push_back("[" + boost::lexical_cast<std::string>(index) + "]");
|
newContext.push_back("[" + boost::lexical_cast<std::string>(index) + "]");
|
||||||
ValidationVisitor<AdapterType> v(arrayItem,
|
ValidationVisitor<AdapterType> v(arrayItem,
|
||||||
newContext, strictTypes, results);
|
newContext, strictTypes, results);
|
||||||
if (index >= constraint.itemSchemas->size()) {
|
if (index >= constraint.itemSchemas.size()) {
|
||||||
if (constraint.additionalItemsSchema) {
|
if (constraint.additionalItemsSchema) {
|
||||||
if (!v.validateSchema(*constraint.additionalItemsSchema)) {
|
if (!v.validateSchema(*constraint.additionalItemsSchema)) {
|
||||||
if (results) {
|
if (results) {
|
||||||
@@ -367,7 +368,7 @@ public:
|
|||||||
boost::lexical_cast<std::string>(index) + " in array due to missing schema.");
|
boost::lexical_cast<std::string>(index) + " in array due to missing schema.");
|
||||||
validated = false;
|
validated = false;
|
||||||
}
|
}
|
||||||
} else if (!v.validateSchema(constraint.itemSchemas->at(index))) {
|
} else if (!v.validateSchema(*constraint.itemSchemas.at(index))) {
|
||||||
if (results) {
|
if (results) {
|
||||||
results->pushError(context, "Failed to validate item #" +
|
results->pushError(context, "Failed to validate item #" +
|
||||||
boost::lexical_cast<std::string>(index) + " against corresponding item schema.");
|
boost::lexical_cast<std::string>(index) + " against corresponding item schema.");
|
||||||
@@ -653,71 +654,17 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate against the multipleOf or divisibleBy constraints
|
* @brief Validate against the multipleOf or divisibleBy constraints
|
||||||
* represented by a MultipleOfDecimalConstraint object.
|
* represented by a MultipleOfConstraint object.
|
||||||
*
|
*
|
||||||
* @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 true if the constraint is satisfied, false otherwise.
|
||||||
*/
|
*/
|
||||||
virtual bool visit(const MultipleOfDecimalConstraint &constraint)
|
virtual bool visit(const MultipleOfConstraint &constraint)
|
||||||
{
|
{
|
||||||
double d = 0.;
|
const int64_t *multipleOfInteger = boost::get<int64_t>(&constraint.value);
|
||||||
|
if (multipleOfInteger) {
|
||||||
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>(
|
|
||||||
constraint.multipleOf));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (target.maybeInteger()) {
|
|
||||||
int64_t i = 0;
|
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>(
|
|
||||||
constraint.multipleOf));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d = static_cast<double>(i);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const double r = 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.
|
|
||||||
*
|
|
||||||
* @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.maybeInteger()) {
|
||||||
if (!target.asInteger(i)) {
|
if (!target.asInteger(i)) {
|
||||||
if (results) {
|
if (results) {
|
||||||
@@ -744,10 +691,10 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i % constraint.multipleOf != 0) {
|
if (i % *multipleOfInteger != 0) {
|
||||||
if (results) {
|
if (results) {
|
||||||
results->pushError(context, "Value should be a multiple of " +
|
results->pushError(context, "Value should be a multiple of " +
|
||||||
boost::lexical_cast<std::string>(constraint.multipleOf));
|
boost::lexical_cast<std::string>(*multipleOfInteger));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -755,6 +702,53 @@ public:
|
|||||||
return true;
|
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, *multipleOfDouble);
|
||||||
|
|
||||||
|
if (fabs(r) > std::numeric_limits<double>::epsilon()) {
|
||||||
|
if (results) {
|
||||||
|
results->pushError(context, "Value should be a multiple of " +
|
||||||
|
boost::lexical_cast<std::string>(*multipleOfDouble));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate against the not constraint represented by a
|
* @brief Validate against the not constraint represented by a
|
||||||
* NotConstraint object.
|
* NotConstraint object.
|
||||||
@@ -791,9 +785,9 @@ public:
|
|||||||
ValidationResults newResults;
|
ValidationResults newResults;
|
||||||
ValidationResults *childResults = (results) ? &newResults : NULL;
|
ValidationResults *childResults = (results) ? &newResults : NULL;
|
||||||
|
|
||||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
BOOST_FOREACH( const Subschema *subschema, constraint.schemas ) {
|
||||||
ValidationVisitor<AdapterType> v(target, context, strictTypes, childResults);
|
ValidationVisitor<AdapterType> v(target, context, strictTypes, childResults);
|
||||||
if (v.validateSchema(schema)) {
|
if (v.validateSchema(*subschema)) {
|
||||||
numValidated++;
|
numValidated++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1033,8 +1027,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FOREACH( const Schema &schema, constraint.schemas ) {
|
BOOST_FOREACH( const Subschema *subschema, constraint.schemas ) {
|
||||||
if (validateSchema(schema)) {
|
if (validateSchema(*subschema)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,38 +14,30 @@ class Schema;
|
|||||||
class ValidationResults;
|
class ValidationResults;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Class that wraps a schema and provides validation functionality.
|
* @brief Class that provides validation functionality.
|
||||||
*
|
|
||||||
* This class wraps a Schema object, and encapsulates the logic required to
|
|
||||||
* validate rapidjson values aginst the schema.
|
|
||||||
*/
|
*/
|
||||||
class Validator
|
class Validator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum TypeCheckingMode
|
||||||
/**
|
|
||||||
* @brief Construct a validator using the specified schema.
|
|
||||||
*
|
|
||||||
* The schema that is provided will be copied.
|
|
||||||
*
|
|
||||||
* @param schema A schema to use for validation
|
|
||||||
*/
|
|
||||||
Validator(const Schema &schema)
|
|
||||||
: schema(new Schema(schema)),
|
|
||||||
strictTypes(true) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set flag to use strict comparison during validation.
|
|
||||||
*
|
|
||||||
* The default value is true, but this can be changed at any time. Future
|
|
||||||
* invokations of validate() will make use of the new value.
|
|
||||||
*
|
|
||||||
* @param strictTypes whether or not to use strict comparison
|
|
||||||
*/
|
|
||||||
void setStrict(bool strictTypes)
|
|
||||||
{
|
{
|
||||||
this->strictTypes = strictTypes;
|
kStrongTypes,
|
||||||
}
|
kWeakTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a Validator that uses strong type checking by default
|
||||||
|
*/
|
||||||
|
Validator()
|
||||||
|
: strictTypes(true) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a Validator using a specific type checking mode
|
||||||
|
*
|
||||||
|
* @param typeCheckingMode choice of strong or weak type checking
|
||||||
|
*/
|
||||||
|
Validator(TypeCheckingMode typeCheckingMode)
|
||||||
|
: strictTypes(typeCheckingMode == kStrongTypes) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate a JSON document and optionally return the results.
|
* @brief Validate a JSON document and optionally return the results.
|
||||||
@@ -58,30 +50,29 @@ public:
|
|||||||
* will only continue for as long as the constraints are validated
|
* will only continue for as long as the constraints are validated
|
||||||
* successfully.
|
* successfully.
|
||||||
*
|
*
|
||||||
* @param target A rapidjson::Value to be validated.
|
* @param schema The schema to validate against
|
||||||
|
* @param target A rapidjson::Value to be validated
|
||||||
*
|
*
|
||||||
* @param results An optional pointer to a ValidationResults instance that
|
* @param results An optional pointer to a ValidationResults instance that
|
||||||
* will be used to report validation errors.
|
* will be used to report validation errors
|
||||||
*
|
*
|
||||||
* @returns true if validation succeeds, false otherwise.
|
* @returns true if validation succeeds, false otherwise
|
||||||
*/
|
*/
|
||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
bool validate(const AdapterType &target, ValidationResults *results)
|
bool validate(const Subschema &schema, const AdapterType &target,
|
||||||
|
ValidationResults *results)
|
||||||
{
|
{
|
||||||
// Construct a ValidationVisitor to perform validation at the root level
|
// Construct a ValidationVisitor to perform validation at the root level
|
||||||
ValidationVisitor<AdapterType> v(target, std::vector<std::string>(1, "<root>"),
|
ValidationVisitor<AdapterType> v(target,
|
||||||
strictTypes, results);
|
std::vector<std::string>(1, "<root>"), strictTypes, results);
|
||||||
|
|
||||||
return v.validateSchema(*schema);
|
return v.validateSchema(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// Pointer to an internal copy of a schema to use for validation
|
|
||||||
boost::scoped_ptr<const Schema> schema;
|
|
||||||
|
|
||||||
/// Flag indicating that strict type comparisons should be used
|
/// Flag indicating that strict type comparisons should be used
|
||||||
bool strictTypes;
|
const bool strictTypes;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -70,12 +70,12 @@ TEST_F(TestFetchDocumentCallback, Basics)
|
|||||||
rapidjson::Document validDocument;
|
rapidjson::Document validDocument;
|
||||||
validDocument.SetObject();
|
validDocument.SetObject();
|
||||||
validDocument.AddMember("test", "valid", allocator);
|
validDocument.AddMember("test", "valid", allocator);
|
||||||
Validator validator(schema);
|
Validator validator;
|
||||||
EXPECT_TRUE(validator.validate(RapidJsonAdapter(validDocument), NULL));
|
EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument), NULL));
|
||||||
|
|
||||||
// Test resulting schema with an invalid document
|
// Test resulting schema with an invalid document
|
||||||
rapidjson::Document invalidDocument;
|
rapidjson::Document invalidDocument;
|
||||||
invalidDocument.SetObject();
|
invalidDocument.SetObject();
|
||||||
invalidDocument.AddMember("test", 123, allocator);
|
invalidDocument.AddMember("test", 123, allocator);
|
||||||
EXPECT_FALSE(validator.validate(RapidJsonAdapter(invalidDocument), NULL));
|
EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument), NULL));
|
||||||
}
|
}
|
||||||
|
@@ -46,9 +46,9 @@ TEST_F(TestValidationErrors, AllOfConstraintFailure)
|
|||||||
ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/documents/array_doubles_1_2_3.json", testDocument) );
|
ASSERT_TRUE( loadDocument(TEST_DATA_DIR "/documents/array_doubles_1_2_3.json", testDocument) );
|
||||||
RapidJsonAdapter testAdapter(testDocument);
|
RapidJsonAdapter testAdapter(testDocument);
|
||||||
|
|
||||||
Validator validator(schema);
|
Validator validator;
|
||||||
ValidationResults results;
|
ValidationResults results;
|
||||||
EXPECT_FALSE( validator.validate(testAdapter, &results) );
|
EXPECT_FALSE( validator.validate(schema, testAdapter, &results) );
|
||||||
|
|
||||||
ValidationResults::Error error;
|
ValidationResults::Error error;
|
||||||
|
|
||||||
|
@@ -96,10 +96,10 @@ protected:
|
|||||||
|
|
||||||
testItr = testObject.find("data");
|
testItr = testObject.find("data");
|
||||||
ASSERT_NE( testObject.end(), testItr );
|
ASSERT_NE( testObject.end(), testItr );
|
||||||
Validator validator(schema);
|
Validator validator(strict ?
|
||||||
validator.setStrict(strict);
|
Validator::kStrongTypes : Validator::kWeakTypes);
|
||||||
|
|
||||||
EXPECT_EQ( shouldValidate, validator.validate(testItr->second, NULL) )
|
EXPECT_EQ( shouldValidate, validator.validate(schema, testItr->second, NULL) )
|
||||||
<< "Failed while testing validate() function in '"
|
<< "Failed while testing validate() function in '"
|
||||||
<< currentTest << "' of test case '"
|
<< currentTest << "' of test case '"
|
||||||
<< currentTestCase << "' with adapter '"
|
<< currentTestCase << "' with adapter '"
|
||||||
|
Reference in New Issue
Block a user