|
@@ -24,7 +24,6 @@ private:
|
|
|
std::string description_;
|
|
std::string description_;
|
|
|
std::unique_ptr<adapter::Const const> default_{nullptr};
|
|
std::unique_ptr<adapter::Const const> default_{nullptr};
|
|
|
|
|
|
|
|
- detail::Reference uri_;
|
|
|
|
|
std::optional<std::string> rejects_all_;
|
|
std::optional<std::string> rejects_all_;
|
|
|
std::optional<schema::Node const *> reference_{};
|
|
std::optional<schema::Node const *> reference_{};
|
|
|
std::unordered_map<std::string, std::unique_ptr<constraint::Constraint>> constraints_{};
|
|
std::unordered_map<std::string, std::unique_ptr<constraint::Constraint>> constraints_{};
|
|
@@ -109,26 +108,11 @@ private:
|
|
|
friend class schema::Node;
|
|
friend class schema::Node;
|
|
|
template <Adapter A> friend class detail::ParserContext;
|
|
template <Adapter A> friend class detail::ParserContext;
|
|
|
|
|
|
|
|
- struct DynamicRef {
|
|
|
|
|
- template <typename F>
|
|
|
|
|
- DynamicRef(detail::Reference const & where, F const & reconstruct)
|
|
|
|
|
- : where(where), reconstruct(reconstruct) {}
|
|
|
|
|
-
|
|
|
|
|
- detail::Reference where;
|
|
|
|
|
- std::function<schema::Node const *()> reconstruct;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
private:
|
|
private:
|
|
|
schema::Node accept_;
|
|
schema::Node accept_;
|
|
|
schema::Node reject_{"always false"};
|
|
schema::Node reject_{"always false"};
|
|
|
|
|
|
|
|
- // 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.
|
|
|
|
|
- // Nothing should be added to this object except through calling
|
|
|
|
|
- // {@see Node::resolve_anchor}, which returns a scope(exit) construct that
|
|
|
|
|
- // cleans up the element.
|
|
|
|
|
- std::map<detail::Anchor, DynamicRef> dynamic_anchors_;
|
|
|
|
|
|
|
+ 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
|
|
@@ -231,19 +215,6 @@ public:
|
|
|
: Schema(adapter::AdapterFor<JSON const>(json), std::forward<Args>(args)...) {}
|
|
: Schema(adapter::AdapterFor<JSON const>(json), std::forward<Args>(args)...) {}
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
- template <Adapter A>
|
|
|
|
|
- void dynamic_anchor(detail::Anchor const & anchor, detail::ParserContext<A> const & context) {
|
|
|
|
|
- dynamic_anchors_.try_emplace(anchor, context.where,
|
|
|
|
|
- [this, context]() { return fetch_schema(context); });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void remove_dynamic_anchor(detail::Anchor const & anchor, detail::Reference const & where) {
|
|
|
|
|
- if (auto it = dynamic_anchors_.find(anchor);
|
|
|
|
|
- it != dynamic_anchors_.end() && it->second.where == where) {
|
|
|
|
|
- dynamic_anchors_.erase(it);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
schema::Node const * alias(detail::Reference const & where, schema::Node const * schema) {
|
|
schema::Node const * alias(detail::Reference const & where, schema::Node const * schema) {
|
|
|
alias_cache_.emplace(where, schema);
|
|
alias_cache_.emplace(where, schema);
|
|
|
return schema;
|
|
return schema;
|
|
@@ -278,7 +249,7 @@ private:
|
|
|
|
|
|
|
|
context.ref.prime(*schema, ref.uri(), context.version,
|
|
context.ref.prime(*schema, ref.uri(), context.version,
|
|
|
context.factory.keywords(context.version));
|
|
context.factory.keywords(context.version));
|
|
|
- ref = context.ref.canonicalize(ref, context.where);
|
|
|
|
|
|
|
+
|
|
|
if (std::optional root = context.ref.root(ref.root())) {
|
|
if (std::optional root = context.ref.root(ref.root())) {
|
|
|
return fetch_schema(context.rebind(ref.pointer().walk(*root), ref));
|
|
return fetch_schema(context.rebind(ref.pointer().walk(*root), ref));
|
|
|
}
|
|
}
|
|
@@ -286,12 +257,6 @@ private:
|
|
|
return fetch_schema(context.rebind(ref.pointer().walk(*schema), ref));
|
|
return fetch_schema(context.rebind(ref.pointer().walk(*schema), ref));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- schema::Node const * resolve_dynamic(detail::Anchor const & ref) {
|
|
|
|
|
- auto it = dynamic_anchors_.find(ref);
|
|
|
|
|
- EXPECT_M(it != dynamic_anchors_.end(), "Unmatched $dynamicRef '" << ref << "'");
|
|
|
|
|
- return it->second.reconstruct();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
template <Adapter A> schema::Node const * fetch_schema(detail::ParserContext<A> const & context) {
|
|
template <Adapter A> schema::Node const * fetch_schema(detail::ParserContext<A> const & context) {
|
|
|
// TODO(samjaffe): No longer promises uniqueness - instead track unique URI's
|
|
// TODO(samjaffe): No longer promises uniqueness - instead track unique URI's
|
|
|
if (std::optional cached = from_cache(context.where)) {
|
|
if (std::optional cached = from_cache(context.where)) {
|
|
@@ -314,16 +279,16 @@ private:
|
|
|
// Do this here first in order to protect from infinite loops
|
|
// Do this here first in order to protect from infinite loops
|
|
|
alias(context.where, &it->second);
|
|
alias(context.where, &it->second);
|
|
|
it->second.construct(context);
|
|
it->second.construct(context);
|
|
|
- if (not it->second.is_pure_reference()) {
|
|
|
|
|
- return &it->second;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /* if (not it->second.is_pure_reference()) { */
|
|
|
|
|
+ return &it->second;
|
|
|
|
|
+ /* } */
|
|
|
|
|
|
|
|
// Special Case - if the only is the reference constraint, then we don't need
|
|
// Special Case - if the only is the reference constraint, then we don't need
|
|
|
// to store it uniquely. Draft2019_09 supports directly extending a $ref schema
|
|
// to store it uniquely. Draft2019_09 supports directly extending a $ref schema
|
|
|
// in the same schema, instead of requiring an allOf clause.
|
|
// in the same schema, instead of requiring an allOf clause.
|
|
|
- schema::Node const * node = *it->second.reference_schema();
|
|
|
|
|
- cache_.erase(it);
|
|
|
|
|
- return alias_cache_[context.where] = node;
|
|
|
|
|
|
|
+ /* schema::Node const * node = *it->second.reference_schema(); */
|
|
|
|
|
+ /* cache_.erase(it); */
|
|
|
|
|
+ /* return alias_cache_[context.where] = node; */
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
@@ -350,18 +315,21 @@ template <Adapter A> detail::OnBlockExit Node::resolve_anchor(detail::ParserCont
|
|
|
return nullptr;
|
|
return nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (context.version == Version::Draft2019_09 && schema.contains("$recursiveAnchor")) {
|
|
|
|
|
- EXPECT_M(schema["$recursiveAnchor"].as_boolean(), "$recursiveAnchor MUST be 'true'");
|
|
|
|
|
-
|
|
|
|
|
- context.root.dynamic_anchor(detail::Anchor(), context);
|
|
|
|
|
- return [&context]() { context.root.remove_dynamic_anchor(detail::Anchor(), context.where); };
|
|
|
|
|
|
|
+ if (context.version == schema::Version::Draft2019_09 && schema.contains("$recursiveAnchor") &&
|
|
|
|
|
+ schema["$recursiveAnchor"].as_boolean()) {
|
|
|
|
|
+ detail::Reference actual_location = context.ref.canonicalize({}, context.where);
|
|
|
|
|
+ if (context.root.dynamic_anchors_.emplace(detail::Anchor(), actual_location).second) {
|
|
|
|
|
+ return [&root = context.root]() { root.dynamic_anchors_.erase(detail::Anchor()); };
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (context.version > Version::Draft2019_09 && schema.contains("$dynamicAnchor")) {
|
|
if (context.version > Version::Draft2019_09 && schema.contains("$dynamicAnchor")) {
|
|
|
detail::Anchor anchor(schema["$dynamicAnchor"].as_string());
|
|
detail::Anchor anchor(schema["$dynamicAnchor"].as_string());
|
|
|
|
|
+ detail::Reference actual_location = context.ref.canonicalize({}, context.where);
|
|
|
|
|
|
|
|
- context.root.dynamic_anchor(anchor, context);
|
|
|
|
|
- return [&context, anchor]() { context.root.remove_dynamic_anchor(anchor, context.where); };
|
|
|
|
|
|
|
+ if (context.root.dynamic_anchors_.emplace(anchor, actual_location).second) {
|
|
|
|
|
+ return [&root = context.root, anchor]() { root.dynamic_anchors_.erase(anchor); };
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
return nullptr;
|
|
@@ -381,18 +349,19 @@ template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (context.version == Version::Draft2019_09 && schema.contains("$recursiveRef")) {
|
|
|
|
|
- detail::Reference ref(schema["$recursiveRef"].as_string());
|
|
|
|
|
- EXPECT_M(ref == detail::Reference(), "Only the root schema is permitted as a $recursiveRef");
|
|
|
|
|
|
|
+ std::string const dyn_ref =
|
|
|
|
|
+ context.version > schema::Version::Draft2019_09 ? "$dynamicRef" : "$recursiveRef";
|
|
|
|
|
+ if (schema.contains(dyn_ref)) {
|
|
|
|
|
+ detail::RootReference ref(schema[dyn_ref].as_string());
|
|
|
|
|
|
|
|
- reference_ = context.root.resolve_dynamic(detail::Anchor());
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (context.version > Version::Draft2019_09 && schema.contains("$dynamicRef")) {
|
|
|
|
|
- detail::Reference ref(schema["$dynamicRef"].as_string());
|
|
|
|
|
-
|
|
|
|
|
- reference_ = context.root.resolve_dynamic(ref.anchor());
|
|
|
|
|
|
|
+ // 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);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ reference_ = context.root.resolve(detail::Reference(ref), context);
|
|
|
|
|
+ }
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|