|
@@ -1,23 +1,25 @@
|
|
|
#pragma once
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <functional>
|
|
#include <functional>
|
|
|
-#include <jvalidate/detail/vocabulary.h>
|
|
|
|
|
#include <map>
|
|
#include <map>
|
|
|
#include <set>
|
|
#include <set>
|
|
|
#include <unordered_map>
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
#include <jvalidate/detail/anchor.h>
|
|
#include <jvalidate/detail/anchor.h>
|
|
|
#include <jvalidate/detail/dynamic_reference_context.h>
|
|
#include <jvalidate/detail/dynamic_reference_context.h>
|
|
|
|
|
+#include <jvalidate/detail/expect.h>
|
|
|
#include <jvalidate/detail/on_block_exit.h>
|
|
#include <jvalidate/detail/on_block_exit.h>
|
|
|
#include <jvalidate/detail/out.h>
|
|
#include <jvalidate/detail/out.h>
|
|
|
#include <jvalidate/detail/parser_context.h>
|
|
#include <jvalidate/detail/parser_context.h>
|
|
|
#include <jvalidate/detail/pointer.h>
|
|
#include <jvalidate/detail/pointer.h>
|
|
|
#include <jvalidate/detail/reference.h>
|
|
#include <jvalidate/detail/reference.h>
|
|
|
#include <jvalidate/detail/reference_cache.h>
|
|
#include <jvalidate/detail/reference_cache.h>
|
|
|
|
|
+#include <jvalidate/detail/vocabulary.h>
|
|
|
#include <jvalidate/document_cache.h>
|
|
#include <jvalidate/document_cache.h>
|
|
|
#include <jvalidate/enum.h>
|
|
#include <jvalidate/enum.h>
|
|
|
#include <jvalidate/forward.h>
|
|
#include <jvalidate/forward.h>
|
|
|
#include <jvalidate/uri.h>
|
|
#include <jvalidate/uri.h>
|
|
|
|
|
+#include <unordered_set>
|
|
|
|
|
|
|
|
namespace jvalidate::detail {
|
|
namespace jvalidate::detail {
|
|
|
template <Adapter A> class ReferenceManager {
|
|
template <Adapter A> class ReferenceManager {
|
|
@@ -25,11 +27,20 @@ public:
|
|
|
using Keywords = std::unordered_map<std::string_view, std::set<schema::Wraps>>;
|
|
using Keywords = std::unordered_map<std::string_view, std::set<schema::Wraps>>;
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
|
|
+ static inline std::map<std::string_view, schema::Version> const g_schema_ids{
|
|
|
|
|
+ {"json-schema.org/draft-04/schema", schema::Version::Draft04},
|
|
|
|
|
+ {"json-schema.org/draft-06/schema", schema::Version::Draft06},
|
|
|
|
|
+ {"json-schema.org/draft-07/schema", schema::Version::Draft07},
|
|
|
|
|
+ {"json-schema.org/draft/2019-09/schema", schema::Version::Draft2019_09},
|
|
|
|
|
+ {"json-schema.org/draft/2020-12/schema", schema::Version::Draft2020_12},
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
ConstraintFactory<A> const & constraints_;
|
|
ConstraintFactory<A> const & constraints_;
|
|
|
DocumentCache<A> & external_;
|
|
DocumentCache<A> & external_;
|
|
|
|
|
|
|
|
ReferenceCache references_;
|
|
ReferenceCache references_;
|
|
|
std::map<schema::Version, Vocabulary<A>> vocabularies_;
|
|
std::map<schema::Version, Vocabulary<A>> vocabularies_;
|
|
|
|
|
+ std::map<URI, Vocabulary<A>> user_vocabularies_;
|
|
|
std::map<RootReference, A> roots_;
|
|
std::map<RootReference, A> roots_;
|
|
|
std::map<URI, std::map<Anchor, Reference>> dynamic_anchors_;
|
|
std::map<URI, std::map<Anchor, Reference>> dynamic_anchors_;
|
|
|
|
|
|
|
@@ -39,7 +50,7 @@ public:
|
|
|
ReferenceManager(DocumentCache<A> & external, A const & root, schema::Version version,
|
|
ReferenceManager(DocumentCache<A> & external, A const & root, schema::Version version,
|
|
|
ConstraintFactory<A> const & constraints)
|
|
ConstraintFactory<A> const & constraints)
|
|
|
: external_(external), constraints_(constraints), roots_{{{}, root}} {
|
|
: external_(external), constraints_(constraints), roots_{{{}, root}} {
|
|
|
- prime(root, {}, vocab(version));
|
|
|
|
|
|
|
+ prime(root, {}, &vocab(version));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Vocabulary<A> const & vocab(schema::Version version) {
|
|
Vocabulary<A> const & vocab(schema::Version version) {
|
|
@@ -49,6 +60,33 @@ public:
|
|
|
return vocabularies_.at(version);
|
|
return vocabularies_.at(version);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ Vocabulary<A> const & vocab(URI schema) {
|
|
|
|
|
+ if (auto it = g_schema_ids.find(schema.resource()); it != g_schema_ids.end()) {
|
|
|
|
|
+ return vocab(it->second);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (auto it = user_vocabularies_.find(schema); it != user_vocabularies_.end()) {
|
|
|
|
|
+ return it->second;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::optional<A> external = external_.try_load(schema);
|
|
|
|
|
+ EXPECT_M(external.has_value(), "Unable to load external meta-schema " << schema);
|
|
|
|
|
+ EXPECT_M(external->type() == adapter::Type::Object, "meta-schema must be an object");
|
|
|
|
|
+
|
|
|
|
|
+ auto metaschema = external->as_object();
|
|
|
|
|
+ EXPECT_M(metaschema.contains("$schema"), "meta-schema must reference an");
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize first to prevent recursion
|
|
|
|
|
+ Vocabulary<A> & parent = user_vocabularies_[schema];
|
|
|
|
|
+ parent = vocab(URI(metaschema["$schema"].as_string()));
|
|
|
|
|
+
|
|
|
|
|
+ if (metaschema.contains("$vocabulary")) {
|
|
|
|
|
+ parent.restrict(extract_keywords(metaschema["$vocabulary"].as_object()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return parent;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
auto dynamic_scope(Reference const & ref) {
|
|
auto dynamic_scope(Reference const & ref) {
|
|
|
URI const uri =
|
|
URI const uri =
|
|
|
ref.pointer().empty() ? ref.uri() : references_.relative_to_nearest_anchor(ref).uri();
|
|
ref.pointer().empty() ? ref.uri() : references_.relative_to_nearest_anchor(ref).uri();
|
|
@@ -67,7 +105,7 @@ public:
|
|
|
|
|
|
|
|
// TODO(samjaffe): Change Versions if needed...
|
|
// TODO(samjaffe): Change Versions if needed...
|
|
|
references_.emplace(ref.uri());
|
|
references_.emplace(ref.uri());
|
|
|
- prime(*external, ref, vocab(version));
|
|
|
|
|
|
|
+ prime(*external, ref, &vocab(version));
|
|
|
|
|
|
|
|
// May have a sub-id that we map to
|
|
// May have a sub-id that we map to
|
|
|
if (auto it = roots_.find(ref.root()); it != roots_.end()) {
|
|
if (auto it = roots_.find(ref.root()); it != roots_.end()) {
|
|
@@ -142,15 +180,20 @@ private:
|
|
|
return active_dynamic_anchors_.lookup(uri, ref.anchor());
|
|
return active_dynamic_anchors_.lookup(uri, ref.anchor());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void prime(Adapter auto const & json, Reference where, Vocabulary<A> const & vocab) {
|
|
|
|
|
|
|
+ void prime(Adapter auto const & json, Reference where, Vocabulary<A> const * vocab) {
|
|
|
if (json.type() != adapter::Type::Object) {
|
|
if (json.type() != adapter::Type::Object) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- canonicalize(where, vocab.version(), json);
|
|
|
|
|
|
|
+ canonicalize(where, vocab->version(), json);
|
|
|
|
|
+
|
|
|
|
|
+ auto schema = json.as_object();
|
|
|
|
|
+ if (schema.contains("$schema")) {
|
|
|
|
|
+ vocab = &this->vocab(URI(schema["$schema"].as_string()));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- for (auto const & [key, value] : json.as_object()) {
|
|
|
|
|
- if (not vocab.is_keyword(key)) {
|
|
|
|
|
|
|
+ for (auto const & [key, value] : schema) {
|
|
|
|
|
+ if (not vocab->is_keyword(key)) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
switch (value.type()) {
|
|
switch (value.type()) {
|
|
@@ -163,7 +206,7 @@ private:
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
case adapter::Type::Object:
|
|
case adapter::Type::Object:
|
|
|
- if (not vocab.is_property_keyword(key)) {
|
|
|
|
|
|
|
+ if (not vocab->is_property_keyword(key)) {
|
|
|
prime(value, where / key, vocab);
|
|
prime(value, where / key, vocab);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
@@ -235,5 +278,21 @@ private:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ std::unordered_set<std::string> extract_keywords(ObjectAdapter auto const & vocabularies) const {
|
|
|
|
|
+ std::unordered_set<std::string> keywords;
|
|
|
|
|
+ for (auto [vocab, enabled] : vocabularies) {
|
|
|
|
|
+ if (not enabled.as_boolean()) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ vocab.replace(vocab.find("/vocab/"), 7, "/meta/");
|
|
|
|
|
+ auto vocab_object = external_.try_load(URI(vocab));
|
|
|
|
|
+ for (auto const & [keyword, _] : vocab_object->as_object()["properties"].as_object()) {
|
|
|
|
|
+ keywords.insert(keyword);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return keywords;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|