Move functionality from Schema class to a new Subschema class (superclass for Schema), and remove dead code

This commit is contained in:
Tristan Penman 2015-12-22 10:26:51 +11:00
parent 0227384534
commit 592e6db083
5 changed files with 264 additions and 270 deletions

View File

@ -50,7 +50,6 @@ add_executable(test_suite
tests/test_property_tree_adapter.cpp tests/test_property_tree_adapter.cpp
tests/test_rapidjson_adapter.cpp tests/test_rapidjson_adapter.cpp
tests/test_picojson_adapter.cpp tests/test_picojson_adapter.cpp
tests/test_uri_resolution.cpp
tests/test_validation_errors.cpp tests/test_validation_errors.cpp
tests/test_validator.cpp tests/test_validator.cpp
) )

View File

@ -1,59 +1,26 @@
#ifndef __VALIJSON_SCHEMA_HPP #ifndef __VALIJSON_SCHEMA_HPP
#define __VALIJSON_SCHEMA_HPP #define __VALIJSON_SCHEMA_HPP
#include <boost/foreach.hpp> #include <valijson/subschema.hpp>
#include <boost/function.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <valijson/constraints/constraint.hpp>
namespace valijson { namespace valijson {
/** /**
* @brief Class that holds a list of Constraints that together form a schema. * Represents the root of a JSON Schema
* *
* This class maintains an internal list of Constraint objects that define a * The root is distinct from other sub-schemas because it is the canonical
* schema. It provides useful functionality such as the ability to easily make * starting point for validation of a document against a given a JSON Schema.
* independent copies of a schema.
*
* Schemas can be modified after construction by adding more constraints, or
* by setting a schema title.
*/ */
class Schema class Schema: public Subschema
{ {
public: public:
/// Typedef the Constraint class into the local namespace for convenience
typedef constraints::Constraint Constraint;
/// Typedef for a function that can be applied to each of the Constraint
/// instances owned by a Schema.
typedef boost::function<bool (const Constraint &)> ApplyFunction;
/** /**
* @brief Construct a new Schema object with no constraints, using the * @brief Construct a new Schema instance with no constraints
* default scope.
*
* The constructed Schema object will have no constraints.
*/ */
Schema() { } Schema() { }
/** /**
* @brief Construct a new Schema object with no constraints, and inherit * @brief Construct a new Schema based an existing Schema instance
* the scope of another Schema.
*
* The functions getScope(), getCanonicalURI() and getInheritedURI() use
* the scope provided by this constructor, unless the URI for this schema
* specifies its own scope.
*
* @param parentScope Scope to inherit.
*/
Schema(const std::string &parentScope)
: parentScope(parentScope) {}
/**
* @brief Construct a new Schema based an existing Schema object.
* *
* The constructed Schema object will contain a copy of each constraint * The constructed Schema object will contain a copy of each constraint
* defined in the referenced Schema. * defined in the referenced Schema.
@ -61,213 +28,8 @@ public:
* @param schema schema to copy constraints from * @param schema schema to copy constraints from
*/ */
Schema(const Schema &schema) Schema(const Schema &schema)
: constraints(schema.constraints), : Subschema(schema) { }
parentScope(schema.parentScope),
title(schema.title) { }
/**
* @brief Add a constraint to the schema.
*
* A copy of the referenced Constraint object will be added to the Schema.
*
* @param constraint Reference to the constraint to copy.
*/
void addConstraint(const Constraint &constraint)
{
constraints.push_back(constraint.clone());
}
/**
* @brief Add a constraint to the schema.
*
* The schema will take ownership of the provided Constraint.
*
* @param constraint Pointer to the constraint to take ownership of.
*/
void addConstraint(Constraint *constraint)
{
constraints.push_back(constraint);
}
/**
* @brief Invoke a function on each constraint in the schema.
*
* This function will apply the callback function to each constraint in
* the schema, even if one of the invokations returns false. If a single
* invokation returns false, this function will return false.
*
* @returns true if all invokations of the callback function are
* successful, false otherwise.
*/
bool apply(ApplyFunction &applyFunction) const
{
bool allTrue = true;
BOOST_FOREACH( const Constraint &constraint, constraints ) {
allTrue = allTrue && applyFunction(constraint);
}
return allTrue;
}
/**
* @brief Invoke a function on each constraint in the schema.
*
* This is a stricter version of the apply() function that will return
* immediately if any of the invokations return false.
*
* @returns true if all invokations of the callback function are
* successful, false otherwise.
*/
bool applyStrict(ApplyFunction &applyFunction) const
{
BOOST_FOREACH( const Constraint &constraint, constraints ) {
if (!applyFunction(constraint)) {
return false;
}
}
return true;
}
/**
* @brief Get the description for this schema
*
* @throw std::runtime_error if the description has not been set
*
* @return schema description
*/
std::string getDescription() const
{
if (description) {
return *description;
}
throw std::runtime_error("Schema does not have a description");
}
std::string getId() const
{
if (id) {
return *id;
}
throw std::runtime_error("id has not been set");
}
std::string getScope() const
{
return std::string();
}
std::string getUri() const
{
return std::string();
}
/**
* @brief Get the title for this schema.
*
* If the title has not been set, this function will throw an exception.
*
* @throw std::runtime_error
*
* @return schema title string
*/
std::string getTitle() const
{
if (title) {
return *title;
}
throw std::runtime_error("Schema does not have a title.");
}
/**
* @brief Returns a boolean value that indicates whether the description
* has been set or not
*
* @return boolean value
*/
bool hasDescription() const
{
return description != boost::none;
}
/**
* @brief Returns a boolean value that indicates whether the id has been
* set or not.
*
* @return boolean value
*/
bool hasId() const
{
return id != boost::none;
}
/**
* @brief Returns a boolean value that indicates whether the schema title
* has been set or not.
*
* @return boolean value
*/
bool hasTitle() const
{
return title != boost::none;
}
std::string resolveUri(const std::string &relative) const
{
return relative;
}
/**
* @brief Set the description for this schema
*
* The description will not be used for validation, but may be used as part
* of the user interface used to interact with a schema.
*
* @param description new description
*/
void setDescription(const std::string &description)
{
this->description = description;
}
void setId(const std::string &id)
{
this->id = id;
}
/**
* @brief Set the title for this schema.
*
* The title is not used for validation, but can be used as part of the
* validation error descriptions that are produced by the Validator and
* ValidationVisitor classes.
*
* @param title new title
*/
void setTitle(const std::string &title)
{
this->title = title;
}
private:
/// List of pointers to constraints that apply to this schema.
boost::ptr_vector<Constraint> constraints;
/// Schema description (optional)
boost::optional<std::string> description;
/// Id to apply when resolving the schema URI.
boost::optional<std::string> id;
/// Scope inherited from a parent schema, or an empty string by default
boost::optional<std::string> parentScope;
/// Title string associated with the schema (optional).
boost::optional<std::string> title;
}; };
} // namespace valijson } // namespace valijson

View File

@ -0,0 +1,254 @@
#ifndef __VALIJSON_SUBSCHEMA_HPP
#define __VALIJSON_SUBSCHEMA_HPP
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <valijson/constraints/constraint.hpp>
namespace valijson {
/**
* Represents a sub-schema within a JSON Schema
*
* While all JSON Schemas have at least one sub-schema, the root, some will
* have additional sub-schemas that are defined as part of constraints that are
* included in the schema. For example, a 'oneOf' constraint maintains a set of
* references to one or more nested sub-schemas. As per the definition of a
* oneOf constraint, a document is valid within that constraint if it validates
* against one of the nested sub-schemas.
*/
class Subschema
{
public:
/// Typedef the Constraint class into the local namespace for convenience
typedef constraints::Constraint Constraint;
/// Typedef for a function that can be applied to each of the Constraint
/// instances owned by a Schema.
typedef boost::function<bool (const Constraint &)> ApplyFunction;
/**
* @brief Construct a new Subschema object with no constraints
*/
Subschema() { }
/**
* @brief Copy an existing Subschema
*
* The constructed Subschema instance will contain a copy of each constraint
* defined in the referenced Subschemas. Constraints will be copied only
* as deep as references to other Subschemas - e.g. copies of constraints
* that refer to sub-schemas, will continue to refer to the same Subschema
* instances.
*
* @param subschema Subschema instance to copy constraints from
*/
Subschema(const Subschema &subschema)
: constraints(subschema.constraints),
title(subschema.title) { }
/**
* @brief Add a constraint to this sub-schema
*
* The constraint will be copied before being added to the list of
* constraints for this Subschema. Note that constraints will be copied
* only as deep as references to other Subschemas - e.g. copies of
* constraints that refer to sub-schemas, will continue to refer to the
* same Subschema instances.
*
* @param constraint Reference to the constraint to copy
*/
void addConstraint(const Constraint &constraint)
{
constraints.push_back(constraint.clone());
}
/**
* @brief Add a constraint to this sub-schema
*
* This Subschema instance will take ownership of Constraint that is
* pointed to, and will free it when it is no longer needed.
*
* @param constraint Pointer to the Constraint to take ownership of
*/
void addConstraint(Constraint *constraint)
{
constraints.push_back(constraint);
}
/**
* @brief Invoke a function on each child Constraint
*
* This function will apply the callback function to each constraint in
* the Subschema, even if one of the invokations returns \c false. However,
* if one or more invokations of the callback function return \c false,
* this function will also return \c false.
*
* @returns \c true if all invokations of the callback function are
* successful, \c false otherwise
*/
bool apply(ApplyFunction &applyFunction) const
{
bool allTrue = true;
BOOST_FOREACH( const Constraint &constraint, constraints ) {
allTrue = allTrue && applyFunction(constraint);
}
return allTrue;
}
/**
* @brief Invoke a function on each child Constraint
*
* This is a stricter version of the apply() function that will return
* immediately if any of the invokations of the callback function return
* \c false.
*
* @returns \c true if all invokations of the callback function are
* successful, \c false otherwise
*/
bool applyStrict(ApplyFunction &applyFunction) const
{
BOOST_FOREACH( const Constraint &constraint, constraints ) {
if (!applyFunction(constraint)) {
return false;
}
}
return true;
}
/**
* @brief Get the description associated with this sub-schema
*
* @throws std::runtime_error if a description has not been set
*
* @returns string containing sub-schema description
*/
std::string getDescription() const
{
if (description) {
return *description;
}
throw std::runtime_error("Schema does not have a description");
}
/**
* @brief Get the ID associated with this sub-schema
*
* @throws std::runtime_error if an ID has not been set
*
* @returns string containing sub-schema ID
*/
std::string getId() const
{
if (id) {
return *id;
}
throw std::runtime_error("Schema does not have an ID");
}
/**
* @brief Get the title associated with this sub-schema
*
* @throws std::runtime_error if a title has not been set
*
* @returns string containing sub-schema title
*/
std::string getTitle() const
{
if (title) {
return *title;
}
throw std::runtime_error("Schema does not have a title");
}
/**
* @brief Check whether this sub-schema has a description
*
* @return boolean value
*/
bool hasDescription() const
{
return description != boost::none;
}
/**
* @brief Check whether this sub-schema has an ID
*
* @return boolean value
*/
bool hasId() const
{
return id != boost::none;
}
/**
* @brief Check whether this sub-schema has a title
*
* @return boolean value
*/
bool hasTitle() const
{
return title != boost::none;
}
/**
* @brief Set the description for this sub-schema
*
* The description will not be used for validation, but may be used as part
* of the user interface for interacting with schemas and sub-schemas. As
* an example, it may be used as part of the validation error descriptions
* that are produced by the Validator and ValidationVisitor classes.
*
* @param description new description
*/
void setDescription(const std::string &description)
{
this->description = description;
}
void setId(const std::string &id)
{
this->id = id;
}
/**
* @brief Set the title for this sub-schema
*
* The title will not be used for validation, but may be used as part
* of the user interface for interacting with schemas and sub-schema. As an
* example, it may be used as part of the validation error descriptions
* that are produced by the Validator and ValidationVisitor classes.
*
* @param title new title
*/
void setTitle(const std::string &title)
{
this->title = title;
}
private:
/// List of pointers to constraints that apply to this schema.
boost::ptr_vector<Constraint> constraints;
/// Schema description (optional)
boost::optional<std::string> description;
/// Id to apply when resolving the schema URI
boost::optional<std::string> id;
/// Title string associated with the schema (optional)
boost::optional<std::string> title;
};
} // namespace valijson
#endif

View File

@ -1,19 +0,0 @@
#include <gtest/gtest.h>
#include <valijson/schema.hpp>
using valijson::Schema;
class TestUriResolution : public ::testing::Test
{
};
TEST_F(TestUriResolution, TestDefaultScopeAndUri)
{
Schema schema;
EXPECT_FALSE( schema.hasId() );
EXPECT_ANY_THROW( schema.getId() );
EXPECT_EQ( "", schema.getUri() );
EXPECT_EQ( "", schema.getScope() );
}

View File

@ -16,7 +16,6 @@
6A725F4817F6404100D6B2FF /* test_property_tree_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AB8FEC417E92B100028E147 /* test_property_tree_adapter.cpp */; }; 6A725F4817F6404100D6B2FF /* test_property_tree_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AB8FEC417E92B100028E147 /* test_property_tree_adapter.cpp */; };
6A725F4917F6404100D6B2FF /* test_rapidjson_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AC18D3917CC874100FE0EC9 /* test_rapidjson_adapter.cpp */; }; 6A725F4917F6404100D6B2FF /* test_rapidjson_adapter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AC18D3917CC874100FE0EC9 /* test_rapidjson_adapter.cpp */; };
6A725F4A17F6404100D6B2FF /* test_validator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AC18D3517CC86E000FE0EC9 /* test_validator.cpp */; }; 6A725F4A17F6404100D6B2FF /* test_validator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AC18D3517CC86E000FE0EC9 /* test_validator.cpp */; };
6A725F4D17F8964B00D6B2FF /* test_uri_resolution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A725F4B17F8956A00D6B2FF /* test_uri_resolution.cpp */; };
6A9E1856194DC44B003F1C4C /* test_fetch_document_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AA8A5DA17F8BDCA002728A0 /* test_fetch_document_callback.cpp */; }; 6A9E1856194DC44B003F1C4C /* test_fetch_document_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AA8A5DA17F8BDCA002728A0 /* test_fetch_document_callback.cpp */; };
6AB8FE8717E6A56F0028E147 /* external_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AB8FE8617E6A56F0028E147 /* external_schema.cpp */; }; 6AB8FE8717E6A56F0028E147 /* external_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AB8FE8617E6A56F0028E147 /* external_schema.cpp */; };
6AB8FE8D17E6A57E0028E147 /* libboost_regex-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A477F8417D6BCBB0013571C /* libboost_regex-mt.dylib */; }; 6AB8FE8D17E6A57E0028E147 /* libboost_regex-mt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A477F8417D6BCBB0013571C /* libboost_regex-mt.dylib */; };
@ -40,6 +39,7 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
6A309D2D1C28C1FD00EF761C /* subschema.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = subschema.hpp; sourceTree = "<group>"; };
6A34698C1BD109A900C97DA2 /* json_reference.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = json_reference.hpp; path = internal/json_reference.hpp; sourceTree = "<group>"; }; 6A34698C1BD109A900C97DA2 /* json_reference.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = json_reference.hpp; path = internal/json_reference.hpp; sourceTree = "<group>"; };
6A477F8417D6BCBB0013571C /* libboost_regex-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_regex-mt.dylib"; path = "/usr/local/lib/libboost_regex-mt.dylib"; sourceTree = "<absolute>"; }; 6A477F8417D6BCBB0013571C /* libboost_regex-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_regex-mt.dylib"; path = "/usr/local/lib/libboost_regex-mt.dylib"; sourceTree = "<absolute>"; };
6A506D121AF884E100C2C818 /* draft-03.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "draft-03.json"; sourceTree = "<group>"; }; 6A506D121AF884E100C2C818 /* draft-03.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "draft-03.json"; sourceTree = "<group>"; };
@ -70,7 +70,6 @@
6A725F4017F61A4400D6B2FF /* object_empty.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = object_empty.json; sourceTree = "<group>"; }; 6A725F4017F61A4400D6B2FF /* object_empty.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = object_empty.json; sourceTree = "<group>"; };
6A725F4217F61AC200D6B2FF /* allof_integers_and_numbers.schema.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = allof_integers_and_numbers.schema.json; sourceTree = "<group>"; }; 6A725F4217F61AC200D6B2FF /* allof_integers_and_numbers.schema.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = allof_integers_and_numbers.schema.json; sourceTree = "<group>"; };
6A725F4317F61B5100D6B2FF /* test_validation_errors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_validation_errors.cpp; sourceTree = "<group>"; }; 6A725F4317F61B5100D6B2FF /* test_validation_errors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_validation_errors.cpp; sourceTree = "<group>"; };
6A725F4B17F8956A00D6B2FF /* test_uri_resolution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_uri_resolution.cpp; sourceTree = "<group>"; };
6A725F6617F89CD500D6B2FF /* abi_prefix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = abi_prefix.hpp; sourceTree = "<group>"; }; 6A725F6617F89CD500D6B2FF /* abi_prefix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = abi_prefix.hpp; sourceTree = "<group>"; };
6A725F6717F89CD500D6B2FF /* abi_suffix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = abi_suffix.hpp; sourceTree = "<group>"; }; 6A725F6717F89CD500D6B2FF /* abi_suffix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = abi_suffix.hpp; sourceTree = "<group>"; };
6A725F6817F89CD500D6B2FF /* config.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = config.hpp; sourceTree = "<group>"; }; 6A725F6817F89CD500D6B2FF /* config.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = config.hpp; sourceTree = "<group>"; };
@ -699,7 +698,6 @@
6AB8FE9217E6BE770028E147 /* test_jsoncpp_adapter.cpp */, 6AB8FE9217E6BE770028E147 /* test_jsoncpp_adapter.cpp */,
6AB8FEC417E92B100028E147 /* test_property_tree_adapter.cpp */, 6AB8FEC417E92B100028E147 /* test_property_tree_adapter.cpp */,
6AC18D3917CC874100FE0EC9 /* test_rapidjson_adapter.cpp */, 6AC18D3917CC874100FE0EC9 /* test_rapidjson_adapter.cpp */,
6A725F4B17F8956A00D6B2FF /* test_uri_resolution.cpp */,
6A725F4317F61B5100D6B2FF /* test_validation_errors.cpp */, 6A725F4317F61B5100D6B2FF /* test_validation_errors.cpp */,
6AC18D3517CC86E000FE0EC9 /* test_validator.cpp */, 6AC18D3517CC86E000FE0EC9 /* test_validator.cpp */,
); );
@ -761,6 +759,7 @@
6A869A2F17CD8A81006864FA /* utils */, 6A869A2F17CD8A81006864FA /* utils */,
6AC78BE917C5FC6A00674114 /* schema.hpp */, 6AC78BE917C5FC6A00674114 /* schema.hpp */,
6AC78BEA17C5FC6A00674114 /* schema_parser.hpp */, 6AC78BEA17C5FC6A00674114 /* schema_parser.hpp */,
6A309D2D1C28C1FD00EF761C /* subschema.hpp */,
6AC78BEC17C5FC6A00674114 /* validation_results.hpp */, 6AC78BEC17C5FC6A00674114 /* validation_results.hpp */,
6AC18C1517C861D600FE0EC9 /* validation_visitor.hpp */, 6AC18C1517C861D600FE0EC9 /* validation_visitor.hpp */,
6AC78BEE17C5FC6A00674114 /* validator.hpp */, 6AC78BEE17C5FC6A00674114 /* validator.hpp */,
@ -1002,7 +1001,6 @@
6A506D201AF88E5D00C2C818 /* test_json_pointer.cpp in Sources */, 6A506D201AF88E5D00C2C818 /* test_json_pointer.cpp in Sources */,
6A725F4917F6404100D6B2FF /* test_rapidjson_adapter.cpp in Sources */, 6A725F4917F6404100D6B2FF /* test_rapidjson_adapter.cpp in Sources */,
6A725F4A17F6404100D6B2FF /* test_validator.cpp in Sources */, 6A725F4A17F6404100D6B2FF /* test_validator.cpp in Sources */,
6A725F4D17F8964B00D6B2FF /* test_uri_resolution.cpp in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };