|
@@ -55,7 +55,7 @@ public:
|
|
|
adapter::Const const * default_value() const { return default_.get(); }
|
|
adapter::Const const * default_value() const { return default_.get(); }
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
- template <Adapter A> detail::OnBlockExit resolve_anchor(detail::ParserContext<A> & context);
|
|
|
|
|
|
|
+ template <Adapter A> detail::OnBlockExit resolve_anchor(detail::ParserContext<A> const & context);
|
|
|
template <Adapter A> bool resolve_reference(detail::ParserContext<A> const & context);
|
|
template <Adapter A> bool resolve_reference(detail::ParserContext<A> const & context);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -112,8 +112,6 @@ private:
|
|
|
schema::Node accept_;
|
|
schema::Node accept_;
|
|
|
schema::Node reject_{"always false"};
|
|
schema::Node reject_{"always false"};
|
|
|
|
|
|
|
|
- std::map<detail::Anchor, detail::Reference> dynamic_anchors_;
|
|
|
|
|
-
|
|
|
|
|
// An owning cache of all created schemas. Avoids storing duplicates such as
|
|
// An owning cache of all created schemas. Avoids storing duplicates such as
|
|
|
// the "always-true" schema, "always-false" schema, and schemas whose only
|
|
// the "always-true" schema, "always-false" schema, and schemas whose only
|
|
|
// meaningful field is "$ref", "$recursiveRef", or "$dynamicRef".
|
|
// meaningful field is "$ref", "$recursiveRef", or "$dynamicRef".
|
|
@@ -162,7 +160,7 @@ public:
|
|
|
ReferenceManager<A> ref(external, json, version, factory.keywords(version));
|
|
ReferenceManager<A> ref(external, json, version, factory.keywords(version));
|
|
|
detail::ParserContext<A> root{*this, json, version, factory, ref};
|
|
detail::ParserContext<A> root{*this, json, version, factory, ref};
|
|
|
|
|
|
|
|
- root.where = root.dynamic_where = ref.canonicalize({}, {});
|
|
|
|
|
|
|
+ root.where = root.dynamic_where = ref.canonicalize({}, {}, false);
|
|
|
construct(root);
|
|
construct(root);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -232,18 +230,16 @@ private:
|
|
|
|
|
|
|
|
template <Adapter A>
|
|
template <Adapter A>
|
|
|
schema::Node const * resolve(detail::Reference const & ref,
|
|
schema::Node const * resolve(detail::Reference const & ref,
|
|
|
- detail::ParserContext<A> const & context,
|
|
|
|
|
- bool is_dynamic_recursion = false) {
|
|
|
|
|
- detail::Reference lexical = context.ref.canonicalize(ref, context.where);
|
|
|
|
|
- detail::Reference dynamic =
|
|
|
|
|
- dynamic_anchors_.empty() || is_dynamic_recursion ? lexical : context.dynamic_where / "$ref";
|
|
|
|
|
|
|
+ detail::ParserContext<A> const & context, bool dynamic_reference) {
|
|
|
|
|
+ detail::Reference lexical = context.ref.canonicalize(ref, context.where, dynamic_reference);
|
|
|
|
|
+ detail::Reference dynamic = dynamic_reference ? lexical : context.dynamic_where / "$ref";
|
|
|
|
|
|
|
|
if (std::optional cached = from_cache(dynamic)) {
|
|
if (std::optional cached = from_cache(dynamic)) {
|
|
|
return *cached;
|
|
return *cached;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Special case if the root-level document does not have an $id property
|
|
// Special case if the root-level document does not have an $id property
|
|
|
- if (std::optional root = context.ref.load(lexical, context)) {
|
|
|
|
|
|
|
+ if (auto [root, scope] = context.ref.load(lexical, context); root.has_value()) {
|
|
|
return fetch_schema(context.rebind(*root, lexical, dynamic));
|
|
return fetch_schema(context.rebind(*root, lexical, dynamic));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -293,51 +289,25 @@ template <Adapter A> schema::Node const * ParserContext<A>::fixed_schema(bool ac
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
namespace jvalidate::schema {
|
|
namespace jvalidate::schema {
|
|
|
-template <Adapter A> detail::OnBlockExit Node::resolve_anchor(detail::ParserContext<A> & context) {
|
|
|
|
|
|
|
+template <Adapter A>
|
|
|
|
|
+detail::OnBlockExit Node::resolve_anchor(detail::ParserContext<A> const & context) {
|
|
|
auto const schema = context.schema.as_object();
|
|
auto const schema = context.schema.as_object();
|
|
|
|
|
|
|
|
if (schema.contains("$anchor") || context.version < schema::Version::Draft2019_09) {
|
|
if (schema.contains("$anchor") || context.version < schema::Version::Draft2019_09) {
|
|
|
return nullptr;
|
|
return nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (context.version == schema::Version::Draft2019_09) {
|
|
|
|
|
- if (not schema.contains("$recursiveAnchor") || not schema["$recursiveAnchor"].as_boolean()) {
|
|
|
|
|
- if (not schema.contains("$id") && not schema.contains("$recursiveAnchor")) {
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- auto it = context.root.dynamic_anchors_.find(detail::Anchor());
|
|
|
|
|
- if (it == context.root.dynamic_anchors_.end()) {
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- detail::Reference where = it->second;
|
|
|
|
|
- context.root.dynamic_anchors_.erase(it);
|
|
|
|
|
-
|
|
|
|
|
- return [&root = context.root, where]() {
|
|
|
|
|
- root.dynamic_anchors_.emplace(detail::Anchor(), where);
|
|
|
|
|
- };
|
|
|
|
|
- } else if (context.root.dynamic_anchors_.emplace(detail::Anchor(), context.where).second) {
|
|
|
|
|
- return [&root = context.root]() { root.dynamic_anchors_.erase(detail::Anchor()); };
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (context.version != schema::Version::Draft2019_09) {
|
|
|
|
|
+ return nullptr;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if (context.version == schema::Version::Draft2019_09 && schema.contains("$recursiveAnchor") &&
|
|
|
|
|
- schema["$recursiveAnchor"].as_boolean()) {
|
|
|
|
|
- if (context.root.dynamic_anchors_.emplace(detail::Anchor(), context.where).second) {
|
|
|
|
|
- return [&root = context.root]() { root.dynamic_anchors_.erase(detail::Anchor()); };
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (schema.contains("$recursiveAnchor") && schema["$recursiveAnchor"].as_boolean()) {
|
|
|
|
|
+ return context.ref.scoped_activate(context.where);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if (context.version > Version::Draft2019_09 && schema.contains("$dynamicAnchor")) {
|
|
|
|
|
- detail::Anchor anchor(schema["$dynamicAnchor"].as_string());
|
|
|
|
|
-
|
|
|
|
|
- if (context.root.dynamic_anchors_.emplace(anchor, context.where).second) {
|
|
|
|
|
- return [&root = context.root, anchor]() { root.dynamic_anchors_.erase(anchor); };
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (not schema.contains("$id") && not schema.contains("$recursiveAnchor")) {
|
|
|
|
|
+ return nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return nullptr;
|
|
|
|
|
|
|
+ return context.ref.suppress(detail::Anchor());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const & context) {
|
|
template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const & context) {
|
|
@@ -346,9 +316,7 @@ template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const
|
|
|
if (schema.contains("$ref")) {
|
|
if (schema.contains("$ref")) {
|
|
|
detail::Reference ref(schema["$ref"].as_string());
|
|
detail::Reference ref(schema["$ref"].as_string());
|
|
|
|
|
|
|
|
- bool is_dynamic_ref_cludge = context.root.dynamic_anchors_.contains(ref.anchor()) &&
|
|
|
|
|
- ref.uri().empty() && ref.pointer().empty();
|
|
|
|
|
- reference_ = context.root.resolve(ref, context, is_dynamic_ref_cludge);
|
|
|
|
|
|
|
+ reference_ = context.root.resolve(ref, context, false);
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -361,14 +329,7 @@ template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const
|
|
|
if (schema.contains(dyn_ref)) {
|
|
if (schema.contains(dyn_ref)) {
|
|
|
detail::Reference ref(schema[dyn_ref].as_string());
|
|
detail::Reference ref(schema[dyn_ref].as_string());
|
|
|
|
|
|
|
|
- // TODO(samjaffe): Relocate...
|
|
|
|
|
- if (auto it = context.root.dynamic_anchors_.find(ref.anchor());
|
|
|
|
|
- it != context.root.dynamic_anchors_.end()) {
|
|
|
|
|
- // TODO(samjaffe): This does not re-compute things...
|
|
|
|
|
- reference_ = context.root.resolve(it->second, context, true);
|
|
|
|
|
- } else {
|
|
|
|
|
- reference_ = context.root.resolve(ref, context);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ reference_ = context.root.resolve(ref, context, true);
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -387,7 +348,7 @@ template <Adapter A> void Node::construct(detail::ParserContext<A> context) {
|
|
|
context.version = schema_version(context.schema);
|
|
context.version = schema_version(context.schema);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- [[maybe_unused]] auto _ = resolve_anchor(context);
|
|
|
|
|
|
|
+ auto _ = resolve_anchor(context);
|
|
|
bool const has_reference = resolve_reference(context);
|
|
bool const has_reference = resolve_reference(context);
|
|
|
|
|
|
|
|
if (schema.contains("default")) {
|
|
if (schema.contains("default")) {
|