Cosmetic improvements for schema_parser.hpp and validation_results.hpp

This commit is contained in:
Tristan Penman 2020-07-06 11:30:41 +10:00
parent 71f4cdaa84
commit 217b990b00
2 changed files with 142 additions and 264 deletions

View File

@ -41,16 +41,13 @@ public:
kDraft7
};
/// Version of JSON Schema that should be expected when parsing
const Version version;
/**
* @brief Construct a new SchemaParser for a given version of JSON Schema
*
* @param version Version of JSON Schema that will be expected
*/
explicit SchemaParser(const Version version = kDraft7)
: version(version) { }
: m_version(version) { }
/**
* @brief Release memory associated with custom ConstraintBuilders
@ -68,14 +65,13 @@ public:
template<typename AdapterType>
struct FunctionPtrs
{
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
DocumentType;
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType DocumentType;
/// Templated function pointer type for fetching remote documents
typedef std::function< const DocumentType* (const std::string &uri) > FetchDoc ;
typedef std::function<const DocumentType* (const std::string &uri)> FetchDoc;
/// Templated function pointer type for freeing fetched documents
typedef std::function< void (const DocumentType *)> FreeDoc ;
typedef std::function<void (const DocumentType *)> FreeDoc;
};
/**
@ -96,8 +92,7 @@ public:
* @todo Add additional checks for key conflicts, empty keys, and
* potential restrictions relating to case sensitivity
*/
void addConstraintBuilder(const std::string &key,
const ConstraintBuilder *builder)
void addConstraintBuilder(const std::string &key, const ConstraintBuilder *builder)
{
constraintBuilders.push_back(std::make_pair(key, builder));
}
@ -122,17 +117,14 @@ public:
typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = nullptr )
{
if ((fetchDoc == nullptr ) ^ (freeDoc == nullptr)) {
throw std::runtime_error(
"Remote document fetching cannot be enabled without both "
"fetch and free functions");
throw std::runtime_error("Remote document fetching can't be enabled without both fetch and free functions");
}
typename DocumentCache<AdapterType>::Type docCache;
SchemaCache schemaCache;
try {
resolveThenPopulateSchema(schema, node, node, schema,
opt::optional<std::string>(), "",
fetchDoc, nullptr, nullptr, docCache, schemaCache);
resolveThenPopulateSchema(schema, node, node, schema, opt::optional<std::string>(), "", fetchDoc, nullptr,
nullptr, docCache, schemaCache);
} catch (...) {
freeDocumentCache<AdapterType>(docCache, freeDoc);
throw;
@ -151,8 +143,7 @@ private:
template<typename AdapterType>
struct DocumentCache
{
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
DocumentType;
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType DocumentType;
typedef std::map<std::string, const DocumentType*> Type;
};
@ -204,16 +195,15 @@ private:
* portions of URI provided by the resolution scope.
*/
virtual opt::optional<std::string> findAbsoluteDocumentUri(
const opt::optional<std::string> resolutionScope,
const opt::optional<std::string> documentUri)
const opt::optional<std::string>& resolutionScope,
const opt::optional<std::string>& documentUri)
{
if (resolutionScope) {
if (documentUri) {
if (internal::uri::isUriAbsolute(*documentUri)) {
return *documentUri;
} else {
return internal::uri::resolveRelativeUri(
*resolutionScope, *documentUri);
return internal::uri::resolveRelativeUri(*resolutionScope, *documentUri);
}
} else {
return *resolutionScope;
@ -248,8 +238,7 @@ private:
if (itr == o.end()) {
return false;
} else if (!itr->second.asString(result)) {
throw std::invalid_argument(
"$ref property expected to contain string value.");
throw std::invalid_argument("$ref property expected to contain string value.");
}
return true;
@ -258,7 +247,7 @@ private:
/**
* Sanitise an optional JSON Pointer, trimming trailing slashes
*/
std::string sanitiseJsonPointer(const opt::optional<std::string> input)
static std::string sanitiseJsonPointer(const opt::optional<std::string>& input)
{
if (input) {
// Trim trailing slash(es)
@ -310,15 +299,14 @@ private:
* usage of the schema cache during development, and is not expected
* to occur otherwise, even for malformed schemas.
*/
void updateSchemaCache(SchemaCache &schemaCache,
static void updateSchemaCache(SchemaCache &schemaCache,
const std::vector<std::string> &keysToCreate,
const Subschema *schema)
{
for (const std::string &keyToCreate : keysToCreate) {
const SchemaCache::value_type value(keyToCreate, schema);
if (!schemaCache.insert(value).second) {
throw std::logic_error(
"Key '" + keyToCreate + "' already in schema cache.");
throw std::logic_error("Key '" + keyToCreate + "' already in schema cache.");
}
}
}
@ -375,16 +363,13 @@ private:
// Construct a key that we can use to search the schema cache for
// a schema corresponding to the current node
const std::string schemaCacheKey =
currentScope ? (*currentScope + nodePath) : nodePath;
const std::string schemaCacheKey = currentScope ? (*currentScope + nodePath) : nodePath;
// Retrieve an existing schema from the cache if possible
const Subschema *cachedPtr =
querySchemaCache(schemaCache, schemaCacheKey);
const Subschema *cachedPtr = querySchemaCache(schemaCache, schemaCacheKey);
// Create a new schema otherwise
const Subschema *subschema = cachedPtr ? cachedPtr :
rootSchema.createSubschema();
const Subschema *subschema = cachedPtr ? cachedPtr : rootSchema.createSubschema();
// Add cache entries for keys belonging to any $ref nodes that were
// visited before arriving at the current node
@ -404,8 +389,7 @@ private:
// Returns a document URI if the reference points somewhere
// other than the current document
const opt::optional<std::string> documentUri =
internal::json_reference::getJsonReferenceUri(jsonRef);
const opt::optional<std::string> documentUri = internal::json_reference::getJsonReferenceUri(jsonRef);
// Extract JSON Pointer from JSON Reference, with any trailing
// slashes removed so that keys in the schema cache end
@ -417,12 +401,10 @@ private:
// scope. An absolute document URI will take precedence when
// present, otherwise we need to resolve the URI relative to
// the current resolution scope
const opt::optional<std::string> actualDocumentUri =
findAbsoluteDocumentUri(currentScope, documentUri);
const opt::optional<std::string> actualDocumentUri = findAbsoluteDocumentUri(currentScope, documentUri);
// Construct a key to search the schema cache for an existing schema
const std::string queryKey = actualDocumentUri ?
(*actualDocumentUri + actualJsonPointer) : actualJsonPointer;
const std::string queryKey = actualDocumentUri ? (*actualDocumentUri + actualJsonPointer) : actualJsonPointer;
// Check for the second termination condition (found a $ref node that
// already has an entry in the schema cache)
@ -441,8 +423,7 @@ private:
if (docCacheItr == docCache.end()) {
// Resolve reference against remote document
if (!fetchDoc) {
throw std::runtime_error(
"Fetching of remote JSON References not enabled.");
throw std::runtime_error("Fetching of remote JSON References not enabled.");
}
// Returns a pointer to the remote document that was
@ -453,9 +434,7 @@ private:
// Can't proceed without the remote document
if (!newDoc) {
throw std::runtime_error(
"Failed to fetch referenced schema document: " +
*actualDocumentUri);
throw std::runtime_error("Failed to fetch referenced schema document: " + *actualDocumentUri);
}
typedef typename DocumentCache<AdapterType>::Type::value_type
@ -579,7 +558,7 @@ private:
const AdapterType &rootNode,
const AdapterType &node,
const Subschema &subschema,
const opt::optional<std::string> currentScope,
const opt::optional<std::string>& currentScope,
const std::string &nodePath,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
const Subschema *parentSubschema,
@ -593,7 +572,7 @@ private:
"appropriate Adapter implementation");
if (!node.isObject()) {
if (version == kDraft7 && node.maybeBool()) {
if (m_version == kDraft7 && node.maybeBool()) {
// Boolean schema
if (!node.asBool()) {
rootSchema.setAlwaysInvalid(&subschema, true);
@ -603,7 +582,7 @@ private:
std::string s;
s += "Expected node at ";
s += nodePath;
if (version == kDraft7) {
if (m_version == kDraft7) {
s += " to contain schema object or boolean value; actual node type is: ";
} else {
s += " to contain schema object; actual node type is: ";
@ -618,15 +597,13 @@ private:
// Check for 'id' attribute and update current scope
opt::optional<std::string> updatedScope;
if ((itr = object.find("id")) != object.end() &&
itr->second.maybeString()) {
if ((itr = object.find("id")) != object.end() && itr->second.maybeString()) {
const std::string id = itr->second.asString();
rootSchema.setSubschemaId(&subschema, itr->second.asString());
if (!currentScope || internal::uri::isUriAbsolute(id)) {
updatedScope = id;
} else {
updatedScope = internal::uri::resolveRelativeUri(
*currentScope, id);
updatedScope = internal::uri::resolveRelativeUri(*currentScope, id);
}
} else {
updatedScope = currentScope;
@ -679,7 +656,7 @@ private:
}
if ((itr = object.find("divisibleBy")) != object.end()) {
if (version == kDraft3) {
if (m_version == kDraft3) {
if (itr->second.maybeInteger()) {
rootSchema.addConstraintToSubschema(
makeMultipleOfIntConstraint(itr->second),
@ -720,10 +697,8 @@ private:
additionalItemsItr = object.find("additionalItems");
rootSchema.addConstraintToSubschema(
makeLinearItemsConstraint(rootSchema, rootNode,
itemsItr != object.end() ?
&itemsItr->second : nullptr,
additionalItemsItr != object.end() ?
&additionalItemsItr->second : nullptr,
itemsItr != object.end() ? &itemsItr->second : nullptr,
additionalItemsItr != object.end() ? &additionalItemsItr->second : nullptr,
updatedScope, nodePath + "/items",
nodePath + "/additionalItems", fetchDoc,
docCache, schemaCache),
@ -738,7 +713,7 @@ private:
const typename AdapterType::Object::const_iterator elseItr = object.find("else");
if (object.end() != ifItr) {
if (version == kDraft7) {
if (m_version == kDraft7) {
rootSchema.addConstraintToSubschema(
makeConditionalConstraint(rootSchema, rootNode,
ifItr->second,
@ -752,7 +727,7 @@ private:
}
}
if (version == kDraft7) {
if (m_version == kDraft7) {
if ((itr = object.find("exclusiveMaximum")) != object.end()) {
rootSchema.addConstraintToSubschema(
makeMaximumConstraintExclusive(itr->second),
@ -773,13 +748,11 @@ private:
&subschema);
} else {
rootSchema.addConstraintToSubschema(
makeMaximumConstraint(itr->second,
&exclusiveMaximumItr->second),
makeMaximumConstraint(itr->second, &exclusiveMaximumItr->second),
&subschema);
}
} else if (object.find("exclusiveMaximum") != object.end()) {
throw std::runtime_error(
"'exclusiveMaximum' constraint only valid if a 'maximum' "
throw std::runtime_error("'exclusiveMaximum' constraint only valid if a 'maximum' "
"constraint is also present");
}
@ -798,11 +771,10 @@ private:
makeMaxPropertiesConstraint(itr->second), &subschema);
}
if (version == kDraft7) {
if (m_version == kDraft7) {
if ((itr = object.find("exclusiveMinimum")) != object.end()) {
rootSchema.addConstraintToSubschema(
makeMinimumConstraintExclusive(itr->second),
&subschema);
makeMinimumConstraintExclusive(itr->second), &subschema);
}
if ((itr = object.find("minimum")) != object.end()) {
@ -811,22 +783,18 @@ private:
&subschema);
}
} else if ((itr = object.find("minimum")) != object.end()) {
typename AdapterType::Object::const_iterator exclusiveMinimumItr =
object.find("exclusiveMinimum");
typename AdapterType::Object::const_iterator exclusiveMinimumItr = object.find("exclusiveMinimum");
if (exclusiveMinimumItr == object.end()) {
rootSchema.addConstraintToSubschema(
makeMinimumConstraint<AdapterType>(itr->second, nullptr),
&subschema);
} else {
rootSchema.addConstraintToSubschema(
makeMinimumConstraint<AdapterType>(
itr->second,
&exclusiveMinimumItr->second),
makeMinimumConstraint<AdapterType>(itr->second, &exclusiveMinimumItr->second),
&subschema);
}
} else if (object.find("exclusiveMinimum") != object.end()) {
throw std::runtime_error(
"'exclusiveMinimum' constraint only valid if a 'minimum' "
throw std::runtime_error("'exclusiveMinimum' constraint only valid if a 'minimum' "
"constraint is also present");
}
@ -846,9 +814,8 @@ private:
}
if ((itr = object.find("multipleOf")) != object.end()) {
if (version == kDraft3) {
throw std::runtime_error(
"'multipleOf' constraint not available in draft 3");
if (m_version == kDraft3) {
throw std::runtime_error("'multipleOf' constraint not available in draft 3");
} else if (itr->second.maybeInteger()) {
rootSchema.addConstraintToSubschema(
makeMultipleOfIntConstraint(itr->second),
@ -858,23 +825,20 @@ private:
makeMultipleOfDoubleConstraint(itr->second),
&subschema);
} else {
throw std::runtime_error("Expected an numeric value for "
" 'divisibleBy' constraint.");
throw std::runtime_error("Expected an numeric value for 'divisibleBy' constraint.");
}
}
if ((itr = object.find("not")) != object.end()) {
rootSchema.addConstraintToSubschema(
makeNotConstraint(rootSchema, rootNode, itr->second,
updatedScope, nodePath + "/not", fetchDoc, docCache,
schemaCache),
makeNotConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/not", fetchDoc,
docCache, schemaCache),
&subschema);
}
if ((itr = object.find("oneOf")) != object.end()) {
rootSchema.addConstraintToSubschema(
makeOneOfConstraint(rootSchema, rootNode, itr->second,
updatedScope, nodePath + "/oneOf", fetchDoc,
makeOneOfConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/oneOf", fetchDoc,
docCache, schemaCache),
&subschema);
}
@ -896,12 +860,9 @@ private:
object.end() != additionalPropertiesItr) {
rootSchema.addConstraintToSubschema(
makePropertiesConstraint(rootSchema, rootNode,
propertiesItr != object.end() ?
&propertiesItr->second : nullptr,
patternPropertiesItr != object.end() ?
&patternPropertiesItr->second : nullptr,
additionalPropertiesItr != object.end() ?
&additionalPropertiesItr->second : nullptr,
propertiesItr != object.end() ? &propertiesItr->second : nullptr,
patternPropertiesItr != object.end() ? &patternPropertiesItr->second : nullptr,
additionalPropertiesItr != object.end() ? &additionalPropertiesItr->second : nullptr,
updatedScope, nodePath + "/properties",
nodePath + "/patternProperties",
nodePath + "/additionalProperties",
@ -911,7 +872,7 @@ private:
}
if ((itr = object.find("propertyNames")) != object.end()) {
if (version == kDraft7) {
if (m_version == kDraft7) {
rootSchema.addConstraintToSubschema(
makePropertyNamesConstraint(rootSchema, rootNode, itr->second, updatedScope,
nodePath, fetchDoc, docCache, schemaCache),
@ -922,60 +883,49 @@ private:
}
if ((itr = object.find("required")) != object.end()) {
if (version == kDraft3) {
if (m_version == kDraft3) {
if (parentSubschema && ownName) {
opt::optional<constraints::RequiredConstraint>
constraint = makeRequiredConstraintForSelf(
itr->second, *ownName);
opt::optional<constraints::RequiredConstraint> constraint =
makeRequiredConstraintForSelf(itr->second, *ownName);
if (constraint) {
rootSchema.addConstraintToSubschema(*constraint,
parentSubschema);
rootSchema.addConstraintToSubschema(*constraint, parentSubschema);
}
} else {
throw std::runtime_error(
"'required' constraint not valid here");
throw std::runtime_error("'required' constraint not valid here");
}
} else {
rootSchema.addConstraintToSubschema(
makeRequiredConstraint(itr->second), &subschema);
rootSchema.addConstraintToSubschema(makeRequiredConstraint(itr->second), &subschema);
}
}
if ((itr = object.find("title")) != object.end()) {
if (itr->second.maybeString()) {
rootSchema.setSubschemaTitle(&subschema,
itr->second.asString());
rootSchema.setSubschemaTitle(&subschema, itr->second.asString());
} else {
throw std::runtime_error(
"'title' attribute should have a string value");
throw std::runtime_error("'title' attribute should have a string value");
}
}
if ((itr = object.find("type")) != object.end()) {
rootSchema.addConstraintToSubschema(
makeTypeConstraint(rootSchema, rootNode, itr->second,
updatedScope, nodePath + "/type", fetchDoc,
makeTypeConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/type", fetchDoc,
docCache, schemaCache),
&subschema);
}
if ((itr = object.find("uniqueItems")) != object.end()) {
opt::optional<constraints::UniqueItemsConstraint> constraint =
makeUniqueItemsConstraint(itr->second);
opt::optional<constraints::UniqueItemsConstraint> constraint = makeUniqueItemsConstraint(itr->second);
if (constraint) {
rootSchema.addConstraintToSubschema(*constraint, &subschema);
}
}
for (ConstraintBuilders::const_iterator
builderItr = constraintBuilders.begin();
builderItr != constraintBuilders.end(); ++builderItr) {
if ((itr = object.find(builderItr->first)) != object.end()) {
for (const auto & constraintBuilder : constraintBuilders) {
if ((itr = object.find(constraintBuilder.first)) != object.end()) {
constraints::Constraint *constraint = nullptr;
try {
constraint = builderItr->second->make(itr->second);
rootSchema.addConstraintToSubschema(*constraint,
&subschema);
constraint = constraintBuilder.second->make(itr->second);
rootSchema.addConstraintToSubschema(*constraint, &subschema);
delete constraint;
} catch (...) {
delete constraint;
@ -1026,16 +976,14 @@ private:
{
std::string jsonRef;
if (!extractJsonReference(node, jsonRef)) {
populateSchema(rootSchema, rootNode, node, subschema, currentScope,
nodePath, fetchDoc, parentSchema, ownName, docCache,
schemaCache);
populateSchema(rootSchema, rootNode, node, subschema, currentScope, nodePath, fetchDoc, parentSchema,
ownName, docCache, schemaCache);
return;
}
// Returns a document URI if the reference points somewhere
// other than the current document
const opt::optional<std::string> documentUri =
internal::json_reference::getJsonReferenceUri(jsonRef);
const opt::optional<std::string> documentUri = internal::json_reference::getJsonReferenceUri(jsonRef);
// Extract JSON Pointer from JSON Reference
const std::string actualJsonPointer = sanitiseJsonPointer(
@ -1044,48 +992,37 @@ private:
if (documentUri && internal::uri::isUriAbsolute(*documentUri)) {
// Resolve reference against remote document
if (!fetchDoc) {
throw std::runtime_error(
"Fetching of remote JSON References not enabled.");
throw std::runtime_error("Fetching of remote JSON References not enabled.");
}
const typename DocumentCache<AdapterType>::DocumentType *newDoc =
fetchDoc(*documentUri);
const typename DocumentCache<AdapterType>::DocumentType *newDoc = fetchDoc(*documentUri);
// Can't proceed without the remote document
if (!newDoc) {
throw std::runtime_error(
"Failed to fetch referenced schema document: " +
*documentUri);
throw std::runtime_error("Failed to fetch referenced schema document: " + *documentUri);
}
// Add to document cache
typedef typename DocumentCache<AdapterType>::Type::value_type
DocCacheValueType;
typedef typename DocumentCache<AdapterType>::Type::value_type DocCacheValueType;
docCache.insert(DocCacheValueType(*documentUri, newDoc));
const AdapterType newRootNode(*newDoc);
const AdapterType &referencedAdapter =
internal::json_pointer::resolveJsonPointer(
newRootNode, actualJsonPointer);
internal::json_pointer::resolveJsonPointer(newRootNode, actualJsonPointer);
// TODO: Need to detect degenerate circular references
resolveThenPopulateSchema(rootSchema, newRootNode,
referencedAdapter, subschema, opt::optional<std::string>(),
actualJsonPointer, fetchDoc, parentSchema, ownName,
docCache, schemaCache);
resolveThenPopulateSchema(rootSchema, newRootNode, referencedAdapter, subschema, {}, actualJsonPointer,
fetchDoc, parentSchema, ownName, docCache, schemaCache);
} else {
const AdapterType &referencedAdapter =
internal::json_pointer::resolveJsonPointer(
rootNode, actualJsonPointer);
internal::json_pointer::resolveJsonPointer(rootNode, actualJsonPointer);
// TODO: Need to detect degenerate circular references
resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter,
subschema, opt::optional<std::string>(),
actualJsonPointer, fetchDoc,
parentSchema, ownName, docCache, schemaCache);
resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter, subschema, {}, actualJsonPointer,
fetchDoc, parentSchema, ownName, docCache, schemaCache);
}
}
@ -1119,15 +1056,14 @@ private:
SchemaCache &schemaCache)
{
if (!node.maybeArray()) {
throw std::runtime_error(
"Expected array value for 'allOf' constraint.");
throw std::runtime_error("Expected array value for 'allOf' constraint.");
}
constraints::AllOfConstraint constraint;
int index = 0;
for (const AdapterType schemaNode : node.asArray()) {
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) {
if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) {
const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, schemaNode, currentScope,
@ -1135,8 +1071,7 @@ private:
constraint.addSubschema(subschema);
index++;
} else {
throw std::runtime_error(
"Expected element to be a valid schema in 'allOf' constraint.");
throw std::runtime_error("Expected element to be a valid schema in 'allOf' constraint.");
}
}
@ -1173,15 +1108,14 @@ private:
SchemaCache &schemaCache)
{
if (!node.maybeArray()) {
throw std::runtime_error(
"Expected array value for 'anyOf' constraint.");
throw std::runtime_error("Expected array value for 'anyOf' constraint.");
}
constraints::AnyOfConstraint constraint;
int index = 0;
for (const AdapterType schemaNode : node.asArray()) {
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) {
if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) {
const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, schemaNode, currentScope,
@ -1189,8 +1123,7 @@ private:
constraint.addSubschema(subschema);
index++;
} else {
throw std::runtime_error(
"Expected array element to be a valid schema in 'anyOf' constraint.");
throw std::runtime_error("Expected array element to be a valid schema in 'anyOf' constraint.");
}
}
@ -1316,7 +1249,7 @@ private:
{
constraints::ContainsConstraint constraint;
if (contains.isObject() || (version == kDraft7 && contains.maybeBool())) {
if (contains.isObject() || (m_version == kDraft7 && contains.maybeBool())) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, contains, currentScope, containsPath,
fetchDoc, nullptr, nullptr, docCache, schemaCache);
@ -1329,8 +1262,7 @@ private:
} else {
// All other formats will result in an exception being thrown.
throw std::runtime_error(
"Expected valid schema for 'contains' constraint.");
throw std::runtime_error("Expected valid schema for 'contains' constraint.");
}
return constraint;
@ -1420,7 +1352,7 @@ private:
// exercised the flexibility by loosely-typed Adapter types. If the
// value of the dependency mapping is an object, then we'll try to
// process it as a dependent schema.
} else if (member.second.isObject() || (version == kDraft7 && member.second.maybeBool())) {
} else if (member.second.isObject() || (m_version == kDraft7 && member.second.maybeBool())) {
// Parse dependent subschema
const Subschema *childSubschema =
makeOrReuseSchema<AdapterType>(rootSchema, rootNode,
@ -1432,7 +1364,7 @@ private:
// If we're supposed to be parsing a Draft3 schema, then the value
// of the dependency mapping can also be a string containing the
// name of a single dependency.
} else if (version == kDraft3 && member.second.isString()) {
} else if (m_version == kDraft3 && member.second.isString()) {
dependenciesConstraint.addPropertyDependency(member.first,
member.second.getString());
@ -1522,8 +1454,7 @@ private:
// and is set to true, then additional array items do not need
// to satisfy any constraints.
if (additionalItems->asBool()) {
constraint.setAdditionalItemsSubschema(
rootSchema.emptySubschema());
constraint.setAdditionalItemsSubschema(rootSchema.emptySubschema());
}
} else if (additionalItems->maybeObject()) {
// If the value of the additionalItems property is an object,
@ -1537,8 +1468,7 @@ private:
} else {
// Any other format for the additionalItems property will result
// in an exception being thrown.
throw std::runtime_error(
"Expected bool or object value for 'additionalItems'");
throw std::runtime_error("Expected bool or object value for 'additionalItems'");
}
} else {
// The default value for the additionalItems property is an empty
@ -1567,9 +1497,7 @@ private:
index++;
}
} else {
throw std::runtime_error(
"Expected array value for non-singular 'items' "
"constraint.");
throw std::runtime_error("Expected array value for non-singular 'items' constraint.");
}
}
@ -1622,7 +1550,7 @@ private:
// array is provided, or a single Schema object, in an object value is
// provided. If the items constraint is not provided, then array items
// will be validated against the additionalItems schema.
if (items.isObject() || (version == kDraft7 && items.maybeBool())) {
if (items.isObject() || (m_version == kDraft7 && items.maybeBool())) {
// If the items constraint contains an object value, then it
// should contain a Schema that will be used to validate all
// items in a target array. Any schema defined by the
@ -1639,8 +1567,7 @@ private:
} else {
// All other formats will result in an exception being thrown.
throw std::runtime_error(
"Expected valid schema for singular 'items' constraint.");
throw std::runtime_error("Expected valid schema for singular 'items' constraint.");
}
return constraint;
@ -1669,8 +1596,7 @@ private:
const AdapterType *exclusiveMaximum)
{
if (!node.maybeDouble()) {
throw std::runtime_error(
"Expected numeric value for maximum constraint.");
throw std::runtime_error("Expected numeric value for maximum constraint.");
}
constraints::MaximumConstraint constraint;
@ -1678,9 +1604,7 @@ private:
if (exclusiveMaximum) {
if (!exclusiveMaximum->maybeBool()) {
throw std::runtime_error(
"Expected boolean value for exclusiveMaximum "
"constraint.");
throw std::runtime_error("Expected boolean value for exclusiveMaximum constraint.");
}
constraint.setExclusiveMaximum(exclusiveMaximum->asBool());
@ -1733,9 +1657,7 @@ private:
}
}
throw std::runtime_error(
"Expected non-negative integer value for 'maxItems' "
"constraint.");
throw std::runtime_error("Expected non-negative integer value for 'maxItems' constraint.");
}
/**
@ -1759,9 +1681,7 @@ private:
}
}
throw std::runtime_error(
"Expected a non-negative integer value for 'maxLength' "
"constraint.");
throw std::runtime_error("Expected a non-negative integer value for 'maxLength' constraint.");
}
/**
@ -1787,9 +1707,7 @@ private:
}
}
throw std::runtime_error(
"Expected a non-negative integer for 'maxProperties' "
"constraint.");
throw std::runtime_error("Expected a non-negative integer for 'maxProperties' constraint.");
}
/**
@ -1810,8 +1728,7 @@ private:
const AdapterType *exclusiveMinimum)
{
if (!node.maybeDouble()) {
throw std::runtime_error(
"Expected numeric value for minimum constraint.");
throw std::runtime_error("Expected numeric value for minimum constraint.");
}
constraints::MinimumConstraint constraint;
@ -1819,9 +1736,7 @@ private:
if (exclusiveMinimum) {
if (!exclusiveMinimum->maybeBool()) {
throw std::runtime_error(
"Expected boolean value for 'exclusiveMinimum' "
"constraint.");
throw std::runtime_error("Expected boolean value for 'exclusiveMinimum' constraint.");
}
constraint.setExclusiveMinimum(exclusiveMinimum->asBool());
@ -1862,8 +1777,7 @@ private:
* @return pointer to a new MinItemsConstraint that belongs to the caller
*/
template<typename AdapterType>
constraints::MinItemsConstraint makeMinItemsConstraint(
const AdapterType &node)
constraints::MinItemsConstraint makeMinItemsConstraint(const AdapterType &node)
{
if (node.maybeInteger()) {
const int64_t value = node.asInteger();
@ -1874,9 +1788,7 @@ private:
}
}
throw std::runtime_error(
"Expected a non-negative integer value for 'minItems' "
"constraint.");
throw std::runtime_error("Expected a non-negative integer value for 'minItems' constraint.");
}
/**
@ -1888,8 +1800,7 @@ private:
* @return pointer to a new MinLengthConstraint that belongs to the caller
*/
template<typename AdapterType>
constraints::MinLengthConstraint makeMinLengthConstraint(
const AdapterType &node)
constraints::MinLengthConstraint makeMinLengthConstraint(const AdapterType &node)
{
if (node.maybeInteger()) {
const int64_t value = node.asInteger();
@ -1900,9 +1811,7 @@ private:
}
}
throw std::runtime_error(
"Expected a non-negative integer value for 'minLength' "
"constraint.");
throw std::runtime_error("Expected a non-negative integer value for 'minLength' constraint.");
}
@ -1917,8 +1826,7 @@ private:
* caller
*/
template<typename AdapterType>
constraints::MinPropertiesConstraint makeMinPropertiesConstraint(
const AdapterType &node)
constraints::MinPropertiesConstraint makeMinPropertiesConstraint(const AdapterType &node)
{
if (node.maybeInteger()) {
int64_t value = node.asInteger();
@ -1929,9 +1837,7 @@ private:
}
}
throw std::runtime_error(
"Expected a non-negative integer for 'minProperties' "
"constraint.");
throw std::runtime_error("Expected a non-negative integer for 'minProperties' constraint.");
}
/**
@ -1943,8 +1849,7 @@ private:
* @return a MultipleOfConstraint
*/
template<typename AdapterType>
constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(
const AdapterType &node)
constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(const AdapterType &node)
{
constraints::MultipleOfDoubleConstraint constraint;
constraint.setDivisor(node.asDouble());
@ -1960,8 +1865,7 @@ private:
* @return a MultipleOfIntConstraint
*/
template<typename AdapterType>
constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(
const AdapterType &node)
constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(const AdapterType &node)
{
constraints::MultipleOfIntConstraint constraint;
constraint.setDivisor(node.asInteger());
@ -1996,7 +1900,7 @@ private:
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
if (node.maybeObject() || (version == kDraft7 && node.maybeBool())) {
if (node.maybeObject() || (m_version == kDraft7 && node.maybeBool())) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, node, currentScope, nodePath,
fetchDoc, nullptr, nullptr, docCache, schemaCache);
@ -2040,8 +1944,7 @@ private:
int index = 0;
for (const AdapterType schemaNode : node.getArray()) {
const std::string childPath = nodePath + "/" +
std::to_string(index);
const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, schemaNode, currentScope, childPath,
fetchDoc, nullptr, nullptr, docCache, schemaCache);
@ -2166,8 +2069,7 @@ private:
// If it has a boolean value that is 'true', then an empty
// schema should be used.
if (additionalProperties->asBool()) {
constraint.setAdditionalPropertiesSubschema(
rootSchema.emptySubschema());
constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema());
}
} else if (additionalProperties->isObject()) {
// If additionalProperties is an object, it should be used as
@ -2179,14 +2081,12 @@ private:
constraint.setAdditionalPropertiesSubschema(subschema);
} else {
// All other types are invalid
throw std::runtime_error(
"Invalid type for 'additionalProperties' constraint.");
throw std::runtime_error("Invalid type for 'additionalProperties' constraint.");
}
} else {
// If an additionalProperties constraint is not provided, then the
// default value is an empty schema.
constraint.setAdditionalPropertiesSubschema(
rootSchema.emptySubschema());
constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema());
}
return constraint;
@ -2203,10 +2103,8 @@ private:
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode,
currentNode, currentScope, nodePath, fetchDoc, nullptr, nullptr, docCache,
schemaCache);
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, currentNode, currentScope,
nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
constraints::PropertyNamesConstraint constraint;
constraint.setSubschema(subschema);
return constraint;
@ -2261,8 +2159,7 @@ private:
for (const AdapterType v : node.getArray()) {
if (!v.maybeString()) {
throw std::runtime_error("Expected required property name to "
"be a string value");
throw std::runtime_error("Expected required property name to be a string value");
}
constraint.addRequiredProperty(v.getString());
@ -2305,9 +2202,8 @@ private:
if (node.maybeString()) {
const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(node.getString());
if (type == TypeConstraint::kAny && version == kDraft4) {
throw std::runtime_error(
"'any' type is not supported in version 4 schemas.");
if (type == TypeConstraint::kAny && m_version == kDraft4) {
throw std::runtime_error("'any' type is not supported in version 4 schemas.");
}
constraint.addNamedType(type);
@ -2316,22 +2212,17 @@ private:
int index = 0;
for (const AdapterType v : node.getArray()) {
if (v.maybeString()) {
const TypeConstraint::JsonType type =
TypeConstraint::jsonTypeFromString(v.getString());
if (type == TypeConstraint::kAny && version == kDraft4) {
throw std::runtime_error(
"'any' type is not supported in version 4 "
"schemas.");
const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(v.getString());
if (type == TypeConstraint::kAny && m_version == kDraft4) {
throw std::runtime_error("'any' type is not supported in version 4 schemas.");
}
constraint.addNamedType(type);
} else if (v.maybeObject() && version == kDraft3) {
} else if (v.maybeObject() && m_version == kDraft3) {
const std::string childPath = nodePath + "/" + std::to_string(index);
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, v, currentScope, childPath,
fetchDoc, nullptr, nullptr, docCache, schemaCache);
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, v, currentScope,
childPath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
constraint.addSchemaType(subschema);
} else {
@ -2341,10 +2232,9 @@ private:
index++;
}
} else if (node.maybeObject() && version == kDraft3) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
rootSchema, rootNode, node, currentScope, nodePath,
fetchDoc, nullptr, nullptr, docCache, schemaCache);
} else if (node.maybeObject() && m_version == kDraft3) {
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, node, currentScope,
nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
constraint.addSchemaType(subschema);
} else {
@ -2376,10 +2266,13 @@ private:
}
}
throw std::runtime_error(
"Expected boolean value for 'uniqueItems' constraint.");
throw std::runtime_error("Expected boolean value for 'uniqueItems' constraint.");
}
private:
/// Version of JSON Schema that should be expected when parsing
const Version m_version;
};
} // namespace valijson

View File

@ -2,6 +2,7 @@
#include <deque>
#include <string>
#include <utility>
#include <vector>
namespace valijson {
@ -25,21 +26,6 @@ public:
*/
struct Error
{
/**
* @brief Construct an Error object with no context or description.
*/
Error() { }
/**
* @brief Construct an Error object using a context and description.
*
* @param context Context string to use
* @param description Description string to use
*/
Error(const std::vector<std::string> &context, const std::string &description)
: context(context),
description(description) { }
/// Path to the node that failed validation.
std::vector<std::string> context;
@ -52,7 +38,7 @@ public:
*/
std::deque<Error>::const_iterator begin() const
{
return errors.begin();
return m_errors.begin();
}
/**
@ -60,7 +46,7 @@ public:
*/
std::deque<Error>::const_iterator end() const
{
return errors.end();
return m_errors.end();
}
/**
@ -68,7 +54,7 @@ public:
*/
size_t numErrors() const
{
return errors.size();
return m_errors.size();
}
/**
@ -78,7 +64,7 @@ public:
*/
void pushError(const Error &error)
{
errors.push_back(error);
m_errors.push_back(error);
}
/**
@ -90,7 +76,7 @@ public:
void
pushError(const std::vector<std::string> &context, const std::string &description)
{
errors.push_back(Error(context, description));
m_errors.push_back({context, description});
}
/**
@ -103,20 +89,19 @@ public:
bool
popError(Error &error)
{
if (errors.empty()) {
if (m_errors.empty()) {
return false;
}
error = errors.front();
errors.pop_front();
error = m_errors.front();
m_errors.pop_front();
return true;
}
private:
/// FIFO queue of validation errors that have been reported
std::deque<Error> errors;
std::deque<Error> m_errors;
};
} // namespace valijson