|
|
@@ -16,6 +16,7 @@
|
|
|
#include <jvalidate/document_cache.h>
|
|
|
#include <jvalidate/enum.h>
|
|
|
#include <jvalidate/forward.h>
|
|
|
+#include <jvalidate/reference_handler.h>
|
|
|
|
|
|
namespace jvalidate::schema {
|
|
|
class Node {
|
|
|
@@ -47,7 +48,7 @@ public:
|
|
|
|
|
|
bool requires_result_context() const { return not post_constraints_.empty(); }
|
|
|
auto const & constraints() const { return constraints_; }
|
|
|
- auto const & post_constraints() const { return constraints_; }
|
|
|
+ auto const & post_constraints() const { return post_constraints_; }
|
|
|
|
|
|
adapter::Const const * default_value() const { return default_.get(); }
|
|
|
|
|
|
@@ -104,6 +105,7 @@ class Schema : public schema::Node {
|
|
|
private:
|
|
|
friend class schema::Node;
|
|
|
template <Adapter A> friend class detail::ParserContext;
|
|
|
+
|
|
|
struct DynamicRef {
|
|
|
template <typename F>
|
|
|
DynamicRef(detail::Reference const & where, F const & reconstruct)
|
|
|
@@ -117,10 +119,6 @@ private:
|
|
|
schema::Node accept_;
|
|
|
schema::Node reject_{"always false"};
|
|
|
|
|
|
- // A map of (URI, Anchor) => (URI, Pointer), binding an anchor reference
|
|
|
- // to it's fully resolved path.
|
|
|
- std::map<detail::Reference, detail::Reference> anchors_;
|
|
|
-
|
|
|
// A map of anchors to DynamicRef info - note that DynamicRef.reconstruct is
|
|
|
// an unsafe object, because it holds an object which may hold references to
|
|
|
// temporary objects.
|
|
|
@@ -174,8 +172,8 @@ public:
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- external.cache_reference(URI(), json);
|
|
|
- detail::ParserContext<A> root{*this, json, version, factory, external};
|
|
|
+ ReferenceManager<A> ref(json, factory.keywords(version));
|
|
|
+ detail::ParserContext<A> root{*this, json, version, factory, external, ref};
|
|
|
construct(root);
|
|
|
}
|
|
|
|
|
|
@@ -230,18 +228,6 @@ public:
|
|
|
: Schema(adapter::AdapterFor<JSON const>(json), std::forward<Args>(args)...) {}
|
|
|
|
|
|
private:
|
|
|
- /**
|
|
|
- * @brief Associate an anchor with its absolute path
|
|
|
- * @pre We should not already have an anchor associated with this anchor
|
|
|
- *
|
|
|
- * @param anchor A URI-Reference containing only a URI and Anchor
|
|
|
- * @param from A URI-Reference representing the absolute path to this Anchor
|
|
|
- */
|
|
|
- void anchor(detail::Reference const & anchor, detail::Reference const & from) {
|
|
|
- EXPECT_M(anchors_.try_emplace(anchor.root(), from).second,
|
|
|
- "more than one anchor found for uri " << anchor);
|
|
|
- }
|
|
|
-
|
|
|
template <Adapter A>
|
|
|
void dynamic_anchor(detail::Anchor const & anchor, detail::ParserContext<A> const & context) {
|
|
|
dynamic_anchors_.try_emplace(anchor, context.where,
|
|
|
@@ -256,16 +242,11 @@ private:
|
|
|
}
|
|
|
|
|
|
schema::Node const * alias(detail::Reference const & where, schema::Node const * schema) {
|
|
|
- EXPECT_M(alias_cache_.try_emplace(where, schema).second,
|
|
|
- "more than one schema found with uri " << where);
|
|
|
+ alias_cache_.emplace(where, schema);
|
|
|
return schema;
|
|
|
}
|
|
|
|
|
|
- std::optional<schema::Node const *> from_cache(detail::Reference ref) {
|
|
|
- if (auto it = anchors_.find(ref.root()); it != anchors_.end()) {
|
|
|
- ref = it->second / ref.pointer();
|
|
|
- }
|
|
|
-
|
|
|
+ std::optional<schema::Node const *> from_cache(detail::Reference const & ref) {
|
|
|
if (auto it = alias_cache_.find(ref); it != alias_cache_.end()) {
|
|
|
return it->second;
|
|
|
}
|
|
|
@@ -275,34 +256,26 @@ private:
|
|
|
|
|
|
template <Adapter A>
|
|
|
schema::Node const * resolve(detail::Reference ref, detail::ParserContext<A> const & context) {
|
|
|
+ ref = context.ref.canonicalize(ref, context.where);
|
|
|
+
|
|
|
// Special case if the root-level document does not have an $id property
|
|
|
- if (ref == detail::Reference() && context.where.uri().empty()) {
|
|
|
- return this;
|
|
|
- }
|
|
|
- if (ref.uri().empty()) {
|
|
|
- ref = detail::Reference(context.where.uri(), ref.anchor(), ref.pointer());
|
|
|
+ if (std::optional root = context.ref.root(ref.root())) {
|
|
|
+ return fetch_schema(context.rebind(ref.pointer().walk(*root), ref));
|
|
|
}
|
|
|
|
|
|
if (std::optional cached = from_cache(ref)) {
|
|
|
return *cached;
|
|
|
}
|
|
|
|
|
|
- // SPECIAL RULE: Resolve this URI into the context of the calling URI
|
|
|
- if (not ref.uri().empty() && ref.uri().scheme().empty()) {
|
|
|
- URI const & relative_to = context.where.uri();
|
|
|
- EXPECT_M(relative_to.resource().rfind('/') != std::string::npos,
|
|
|
- "Unable to deduce root for relative uri " << ref.uri() << " (" << relative_to
|
|
|
- << ")");
|
|
|
- ref = detail::Reference(relative_to.parent() / ref.uri(), ref.anchor(), ref.pointer());
|
|
|
- }
|
|
|
-
|
|
|
- std::optional schema = context.external.try_load(ref);
|
|
|
+ std::optional schema = context.external.try_load(ref.uri());
|
|
|
if (not schema.has_value()) {
|
|
|
std::string error = "URIResolver could not resolve " + std::string(ref.uri());
|
|
|
return alias(ref, &cache_.try_emplace(ref, error).first->second);
|
|
|
}
|
|
|
|
|
|
- return fetch_schema(context.rebind(*schema, ref));
|
|
|
+ context.ref.prime(*schema, ref.root(), context.factory.keywords(context.version));
|
|
|
+ ref = context.ref.canonicalize(ref, context.where);
|
|
|
+ return fetch_schema(context.rebind(ref.pointer().walk(*schema), ref));
|
|
|
}
|
|
|
|
|
|
schema::Node const * resolve_dynamic(detail::Anchor const & ref) {
|
|
|
@@ -366,12 +339,6 @@ template <Adapter A> detail::OnBlockExit Node::resolve_anchor(detail::ParserCont
|
|
|
auto const schema = context.schema.as_object();
|
|
|
|
|
|
if (schema.contains("$anchor")) {
|
|
|
- // Create an anchor mapping using the current document and the anchor
|
|
|
- // string. There's no need for special validation/chaining here, because
|
|
|
- // {@see Schema::resolve} will turn all $ref/$dynamicRef anchors into
|
|
|
- // their fully-qualified path.
|
|
|
- detail::Anchor anchor(schema["$anchor"].as_string());
|
|
|
- context.root.anchor(detail::Reference(context.where.uri(), anchor), context.where);
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
@@ -436,18 +403,6 @@ template <Adapter A> void Node::construct(detail::ParserContext<A> context) {
|
|
|
context.version = schema_version(context.schema);
|
|
|
}
|
|
|
|
|
|
- if (schema.contains("$id")) {
|
|
|
- detail::Reference id(schema["$id"].as_string(), false);
|
|
|
- if (id.uri().scheme().empty() and not context.where.uri().empty()) {
|
|
|
- id = detail::Reference(context.where.uri().parent() / id.uri(), {}, id.pointer());
|
|
|
- }
|
|
|
-
|
|
|
- if (id != context.where) {
|
|
|
- context.external.cache_reference(id.uri(), context.schema);
|
|
|
- context.root.alias(context.where = id, this);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
[[maybe_unused]] auto _ = resolve_anchor(context);
|
|
|
bool const has_reference = resolve_reference(context);
|
|
|
|