mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-03 04:38:40 +01:00
297 lines
8.2 KiB
C++
297 lines
8.2 KiB
C++
#pragma once
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <valijson/constraints/constraint.hpp>
|
|
#include <valijson/internal/optional.hpp>
|
|
#include <valijson/exceptions.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 for custom new-/malloc-like function
|
|
typedef void * (*CustomAlloc)(size_t size);
|
|
|
|
/// Typedef for custom free-like function
|
|
typedef void (*CustomFree)(void *);
|
|
|
|
/// 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 std::function<bool (const Constraint &)> ApplyFunction;
|
|
|
|
// Disable copy construction
|
|
Subschema(const Subschema &) = delete;
|
|
|
|
// Disable copy assignment
|
|
Subschema & operator=(const Subschema &) = delete;
|
|
|
|
/**
|
|
* @brief Construct a new Subschema object
|
|
*/
|
|
Subschema()
|
|
: m_allocFn([](size_t size) { return ::operator new(size, std::nothrow); })
|
|
, m_freeFn(::operator delete)
|
|
, m_alwaysInvalid(false) { }
|
|
|
|
/**
|
|
* @brief Construct a new Subschema using custom memory management
|
|
* functions
|
|
*
|
|
* @param allocFn malloc- or new-like function to allocate memory
|
|
* within Schema, such as for Subschema instances
|
|
* @param freeFn free-like function to free memory allocated with
|
|
* the `customAlloc` function
|
|
*/
|
|
Subschema(CustomAlloc allocFn, CustomFree freeFn)
|
|
: m_allocFn(allocFn)
|
|
, m_freeFn(freeFn)
|
|
, m_alwaysInvalid(false)
|
|
{
|
|
// explicitly initialise optionals. See: https://github.com/tristanpenman/valijson/issues/124
|
|
m_description = opt::nullopt;
|
|
m_id = opt::nullopt;
|
|
m_title = opt::nullopt;
|
|
}
|
|
|
|
/**
|
|
* @brief Clean up and free all memory managed by the Subschema
|
|
*/
|
|
virtual ~Subschema()
|
|
{
|
|
#if VALIJSON_USE_EXCEPTIONS
|
|
try {
|
|
#endif
|
|
m_constraints.clear();
|
|
#if VALIJSON_USE_EXCEPTIONS
|
|
} catch (const std::exception &e) {
|
|
fprintf(stderr, "Caught an exception in Subschema destructor: %s",
|
|
e.what());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
// the vector allocation might throw but the constraint memory will be taken care of anyways
|
|
m_constraints.push_back(constraint.clone(m_allocFn, m_freeFn));
|
|
}
|
|
|
|
/**
|
|
* @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 invocations returns \c false. However,
|
|
* if one or more invocations of the callback function return \c false,
|
|
* this function will also return \c false.
|
|
*
|
|
* @returns \c true if all invocations of the callback function are
|
|
* successful, \c false otherwise
|
|
*/
|
|
bool apply(ApplyFunction &applyFunction) const
|
|
{
|
|
bool allTrue = true;
|
|
for (auto &&constraint : m_constraints) {
|
|
allTrue = applyFunction(*constraint) && allTrue;
|
|
}
|
|
|
|
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 invocations of the callback function return
|
|
* \c false.
|
|
*
|
|
* @returns \c true if all invocations of the callback function are
|
|
* successful, \c false otherwise
|
|
*/
|
|
bool applyStrict(ApplyFunction &applyFunction) const
|
|
{
|
|
for (auto &&constraint : m_constraints) {
|
|
if (!applyFunction(*constraint)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool getAlwaysInvalid() const
|
|
{
|
|
return m_alwaysInvalid;
|
|
}
|
|
|
|
/**
|
|
* @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 (m_description) {
|
|
return *m_description;
|
|
}
|
|
|
|
throwRuntimeError("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 (m_id) {
|
|
return *m_id;
|
|
}
|
|
|
|
throwRuntimeError("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 (m_title) {
|
|
return *m_title;
|
|
}
|
|
|
|
throwRuntimeError("Schema does not have a title");
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether this sub-schema has a description
|
|
*
|
|
* @return boolean value
|
|
*/
|
|
bool hasDescription() const
|
|
{
|
|
return static_cast<bool>(m_description);
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether this sub-schema has an ID
|
|
*
|
|
* @return boolean value
|
|
*/
|
|
bool hasId() const
|
|
{
|
|
return static_cast<bool>(m_id);
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether this sub-schema has a title
|
|
*
|
|
* @return boolean value
|
|
*/
|
|
bool hasTitle() const
|
|
{
|
|
return static_cast<bool>(m_title);
|
|
}
|
|
|
|
void setAlwaysInvalid(bool value)
|
|
{
|
|
m_alwaysInvalid = value;
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
m_description = description;
|
|
}
|
|
|
|
void setId(const std::string &id)
|
|
{
|
|
m_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)
|
|
{
|
|
m_title = title;
|
|
}
|
|
|
|
protected:
|
|
|
|
CustomAlloc m_allocFn;
|
|
|
|
CustomFree m_freeFn;
|
|
|
|
private:
|
|
|
|
bool m_alwaysInvalid;
|
|
|
|
/// List of pointers to constraints that apply to this schema.
|
|
std::vector<Constraint::OwningPointer> m_constraints;
|
|
|
|
/// Schema description (optional)
|
|
opt::optional<std::string> m_description;
|
|
|
|
/// Id to apply when resolving the schema URI
|
|
opt::optional<std::string> m_id;
|
|
|
|
/// Title string associated with the schema (optional)
|
|
opt::optional<std::string> m_title;
|
|
};
|
|
|
|
} // namespace valijson
|