Remove shared_ptrs from fetch document interface, and add a 'free' callback that is used to release memory after parsing

This commit is contained in:
Tristan Penman 2016-02-13 20:52:23 -08:00
parent 348ba26596
commit 7f4db481c7
2 changed files with 92 additions and 49 deletions

View File

@ -57,10 +57,16 @@ public:
* @brief Struct to contain templated function type for fetching documents
*/
template<typename AdapterType>
struct FetchDocumentFunction {
/// Functor for dereferencing JSON References
typedef boost::function<boost::shared_ptr<const AdapterType>(
const std::string &uri)> Type;
struct FunctionPtrs
{
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
DocumentType;
/// Templated function pointer type for fetching remote documents
typedef const AdapterType * (*FetchDoc)(const std::string &uri);
/// Templated function pointer type for freeing fetched documents
typedef void (*FreeDoc)(const AdapterType *);
};
/**
@ -79,13 +85,26 @@ public:
void populateSchema(
const AdapterType &node,
Schema &schema,
boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc = boost::none)
typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc = NULL,
typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = NULL)
{
if ((fetchDoc == NULL) ^ (freeDoc == NULL)) {
throw std::runtime_error(
"Remote document fetching cannot be enabled without both "
"fetch and free functions");
}
typename DocumentCache<AdapterType>::Type docCache;
SchemaCache schemaCache;
populateSchema(schema, node, node, schema, boost::none, "", fetchDoc,
NULL, NULL, docCache, schemaCache);
try {
populateSchema(schema, node, node, schema, boost::none, "",
fetchDoc, NULL, NULL, docCache, schemaCache);
} catch (...) {
freeDocumentCache<AdapterType>(docCache, freeDoc);
throw;
}
freeDocumentCache<AdapterType>(docCache, freeDoc);
}
private:
@ -96,11 +115,31 @@ private:
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
DocumentType;
typedef std::map<std::string, const DocumentType*> Type;
typedef std::map<std::string, const AdapterType*> Type;
};
typedef std::map<std::string, boost::shared_ptr<Schema> > SchemaCache;
/**
* @brief Free memory used by fetched documents
*
* If a custom 'free' function has not been provided, then the default
* delete operator will be used.
*
* @param docCache collection of fetched documents to free
* @param freeDoc optional custom free function
*/
template<typename AdapterType>
void freeDocumentCache(const typename DocumentCache<AdapterType>::Type
&docCache, typename FunctionPtrs<AdapterType>::FreeDoc freeDoc)
{
typedef typename DocumentCache<AdapterType>::Type DocCacheType;
BOOST_FOREACH( const typename DocCacheType::value_type &v, docCache ) {
freeDoc(v.second);
}
}
/**
* @brief Populate a Schema object from JSON Schema document
*
@ -135,8 +174,7 @@ private:
const Subschema &subschema,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
const Subschema *parentSubschema,
const std::string *ownName,
typename DocumentCache<AdapterType>::Type &docCache,
@ -473,8 +511,7 @@ private:
const Subschema &subschema,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
const Subschema *parentSubschema,
const std::string *ownName,
typename DocumentCache<AdapterType>::Type &docCache,
@ -496,11 +533,14 @@ private:
"Support for JSON References not enabled.");
}
const AdapterType * docPtr = NULL;
const typename DocumentCache<AdapterType>::Type::const_iterator
docCacheItr = docCache.find(*documentUri);
if (docCacheItr == docCache.end()) {
// Returns a shared pointer to the remote document that was
// retrieved, or null if retrieval failed. The resulting document
// must remain in scope until populateSchema returns.
boost::shared_ptr<const AdapterType> docPtr =
(*fetchDoc)(*documentUri);
// retrieved, or null if retrieval failed. The resulting
// document must remain in scope until populateSchema returns.
docPtr = (*fetchDoc)(*documentUri);
// Can't proceed without the remote document
if (!docPtr) {
@ -508,6 +548,14 @@ private:
"Failed to fetch referenced schema document.");
}
// TODO: If this fails, how would the document be freed?
docCache.insert(
typename DocumentCache<AdapterType>::Type::value_type(
*documentUri, docPtr));
} else {
docPtr = docCacheItr->second;
}
const AdapterType &ref = internal::json_pointer::resolveJsonPointer(
*docPtr, jsonPointer);
@ -552,8 +600,7 @@ private:
const AdapterType &node,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -610,8 +657,7 @@ private:
const AdapterType &node,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -686,8 +732,7 @@ private:
const AdapterType &node,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -816,8 +861,7 @@ private:
const boost::optional<std::string> currentScope,
const std::string &itemsPath,
const std::string &additionalItemsPath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -923,8 +967,7 @@ private:
const AdapterType &items,
const boost::optional<std::string> currentScope,
const std::string &itemsPath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -1260,8 +1303,7 @@ private:
const AdapterType &node,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -1303,8 +1345,7 @@ private:
const AdapterType &node,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{
@ -1390,8 +1431,7 @@ private:
const std::string &propertiesPath,
const std::string &patternPropertiesPath,
const std::string &additionalPropertiesPath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
const Subschema *parentSubschema,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
@ -1551,8 +1591,7 @@ private:
const AdapterType &node,
const boost::optional<std::string> currentScope,
const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type>
fetchDoc,
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache)
{

View File

@ -13,9 +13,6 @@ using valijson::SchemaParser;
using valijson::adapters::RapidJsonAdapter;
using valijson::Validator;
typedef SchemaParser::FetchDocumentFunction<RapidJsonAdapter>::Type
FetchDocumentFunction;
namespace {
static rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> allocator;
@ -29,7 +26,7 @@ class TestFetchDocumentCallback : public ::testing::Test
};
boost::shared_ptr<const RapidJsonAdapter> fetchDocument(const std::string &uri)
const RapidJsonAdapter * fetchDocument(const std::string &uri)
{
EXPECT_STREQ("test", uri.c_str());
@ -49,7 +46,12 @@ boost::shared_ptr<const RapidJsonAdapter> fetchDocument(const std::string &uri)
// Have to ensure that fetchedRoot exists for at least as long as the
// shared pointer that we return here
return boost::make_shared<RapidJsonAdapter>(fetchedRoot);
return new RapidJsonAdapter(fetchedRoot);
}
void freeDocument(const RapidJsonAdapter *adapter)
{
delete adapter;
}
TEST_F(TestFetchDocumentCallback, Basics)
@ -63,19 +65,21 @@ TEST_F(TestFetchDocumentCallback, Basics)
// Parse schema document
Schema schema;
SchemaParser schemaParser;
schemaParser.populateSchema(schemaDocumentAdapter, schema,
boost::make_optional<FetchDocumentFunction>(fetchDocument));
schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchDocument,
freeDocument);
// Test resulting schema with a valid document
rapidjson::Document validDocument;
validDocument.SetObject();
validDocument.AddMember("test", "valid", allocator);
Validator validator;
EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument), NULL));
EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument),
NULL));
// Test resulting schema with an invalid document
rapidjson::Document invalidDocument;
invalidDocument.SetObject();
invalidDocument.AddMember("test", 123, allocator);
EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument), NULL));
EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument),
NULL));
}