Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions src/core/jsonschema/bundle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@

namespace {

auto is_official_metaschema_reference(const sourcemeta::core::Pointer &pointer,
const std::string &destination) -> bool {
auto is_official_metaschema_reference(
const sourcemeta::core::WeakPointer &pointer,
const std::string &destination) -> bool {
assert(!pointer.empty());
assert(pointer.back().is_property());
return pointer.back().to_property() == "$schema" &&
Expand Down Expand Up @@ -47,7 +48,8 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema,

if (reference.base.empty()) {
throw sourcemeta::core::SchemaReferenceError(
reference.destination, pointer, "Could not resolve schema reference");
reference.destination, sourcemeta::core::to_pointer(pointer),
"Could not resolve schema reference");
}

// To not infinitely loop on circular references
Expand All @@ -59,7 +61,8 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema,
// find the base, then we are facing an unresolved fragment
if (frame.traverse(reference.base).has_value()) {
throw sourcemeta::core::SchemaReferenceError(
reference.destination, pointer, "Could not resolve schema reference");
reference.destination, sourcemeta::core::to_pointer(pointer),
"Could not resolve schema reference");
}

assert(!reference.base.empty());
Expand All @@ -72,14 +75,16 @@ auto dependencies_internal(const sourcemeta::core::JSON &schema,

if (!sourcemeta::core::is_schema(remote.value())) {
throw sourcemeta::core::SchemaReferenceError(
identifier, pointer, "The JSON document is not a valid JSON Schema");
identifier, sourcemeta::core::to_pointer(pointer),
"The JSON document is not a valid JSON Schema");
}

const auto remote_base_dialect{sourcemeta::core::base_dialect(
remote.value(), resolver, default_dialect)};
if (!remote_base_dialect.has_value()) {
throw sourcemeta::core::SchemaReferenceError(
identifier, pointer, "The JSON document is not a valid JSON Schema");
identifier, sourcemeta::core::to_pointer(pointer),
"The JSON document is not a valid JSON Schema");
}

callback(origin, pointer, identifier, remote.value());
Expand Down Expand Up @@ -161,12 +166,14 @@ auto bundle_schema(sourcemeta::core::JSON &root,
// find base, then we are facing an unresolved fragment
if (!reference.base.empty() && frame.traverse(reference.base).has_value()) {
throw sourcemeta::core::SchemaReferenceError(
reference.destination, pointer, "Could not resolve schema reference");
reference.destination, sourcemeta::core::to_pointer(pointer),
"Could not resolve schema reference");
}

if (reference.base.empty()) {
throw sourcemeta::core::SchemaReferenceError(
reference.destination, pointer, "Could not resolve schema reference");
reference.destination, sourcemeta::core::to_pointer(pointer),
"Could not resolve schema reference");
}

assert(!reference.base.empty());
Expand All @@ -182,7 +189,7 @@ auto bundle_schema(sourcemeta::core::JSON &root,
if (!remote.has_value()) {
if (frame.traverse(identifier).has_value()) {
throw sourcemeta::core::SchemaReferenceError(
reference.destination, pointer,
reference.destination, sourcemeta::core::to_pointer(pointer),
"Could not resolve schema reference");
}

Expand All @@ -192,14 +199,16 @@ auto bundle_schema(sourcemeta::core::JSON &root,

if (!sourcemeta::core::is_schema(remote.value())) {
throw sourcemeta::core::SchemaReferenceError(
identifier, pointer, "The JSON document is not a valid JSON Schema");
identifier, sourcemeta::core::to_pointer(pointer),
"The JSON document is not a valid JSON Schema");
}

const auto remote_base_dialect{sourcemeta::core::base_dialect(
remote.value(), resolver, default_dialect)};
if (!remote_base_dialect.has_value()) {
throw sourcemeta::core::SchemaReferenceError(
identifier, pointer, "The JSON document is not a valid JSON Schema");
identifier, sourcemeta::core::to_pointer(pointer),
"The JSON document is not a valid JSON Schema");
}

// If the reference has a fragment, verify it exists in the remote
Expand All @@ -213,7 +222,7 @@ auto bundle_schema(sourcemeta::core::JSON &root,
identifier);
if (!remote_frame.traverse(reference.destination).has_value()) {
throw sourcemeta::core::SchemaReferenceError(
reference.destination, pointer,
reference.destination, sourcemeta::core::to_pointer(pointer),
"Could not resolve schema reference");
}
}
Expand Down
37 changes: 25 additions & 12 deletions src/core/jsonschema/frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@

enum class AnchorType : std::uint8_t { Static, Dynamic, All };

// Static keyword strings for reference pointers
static const std::string KEYWORD_SCHEMA{"$schema"};
static const std::string KEYWORD_REF{"$ref"};
static const std::string KEYWORD_RECURSIVE_REF{"$recursiveRef"};
static const std::string KEYWORD_DYNAMIC_REF{"$dynamicRef"};

namespace {

auto find_anchors(const sourcemeta::core::JSON &schema,
Expand Down Expand Up @@ -400,8 +406,8 @@ auto SchemaFrame::to_json(

if (tracker.has_value()) {
entry.assign_assume_new("position",
sourcemeta::core::to_json(
tracker.value().get(reference.first.second)));
sourcemeta::core::to_json(tracker.value().get(
to_pointer(reference.first.second))));
} else {
entry.assign_assume_new("position", sourcemeta::core::to_json(nullptr));
}
Expand Down Expand Up @@ -527,7 +533,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
for (const auto &entry_index : current_subschema_entries) {
const auto &entry{subschema_entries[entry_index]};
const auto &common_pointer_weak{entry.common.pointer};
const auto common_pointer{to_pointer(common_pointer_weak)};
const auto &common_parent{entry.common.parent};
if (entry.id.has_value()) {
assert(entry.common.base_dialect.has_value());
Expand Down Expand Up @@ -618,8 +623,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,

metaschema.canonicalize();
assert(entry.common.subschema.get().defines("$schema"));
auto schema_pointer{common_pointer_weak};
schema_pointer.push_back(std::cref(KEYWORD_SCHEMA));
const auto [it, inserted] = this->references_.insert_or_assign(
{SchemaReferenceType::Static, common_pointer.concat({"$schema"})},
{SchemaReferenceType::Static, std::move(schema_pointer)},
SchemaFrame::ReferencesEntry{.original = maybe_metaschema,
.destination =
metaschema.recompose(),
Expand Down Expand Up @@ -809,7 +816,6 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
// Resolve references after all framing was performed
for (const auto &entry : subschema_entries) {
const auto &common_pointer_weak{entry.common.pointer};
const auto common_pointer{to_pointer(common_pointer_weak)};
if (entry.common.subschema.get().is_object()) {
const auto nearest_bases{find_nearest_bases(
base_uris, common_pointer_weak,
Expand All @@ -825,8 +831,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
}

ref.canonicalize();
auto ref_pointer{common_pointer_weak};
ref_pointer.push_back(std::cref(KEYWORD_REF));
const auto [it, inserted] = this->references_.insert_or_assign(
{SchemaReferenceType::Static, common_pointer.concat({"$ref"})},
{SchemaReferenceType::Static, std::move(ref_pointer)},
SchemaFrame::ReferencesEntry{.original = original,
.destination = ref.recompose(),
.base = std::string_view{},
Expand All @@ -848,7 +856,8 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
// https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.8.2.4.2.1
if (ref != "#") {
throw sourcemeta::core::SchemaReferenceError(
entry.id.value_or(""), common_pointer.concat({"$recursiveRef"}),
entry.id.value_or(""),
to_pointer(common_pointer_weak).concat({"$recursiveRef"}),
"Invalid recursive reference");
}

Expand All @@ -860,8 +869,10 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
? SchemaReferenceType::Static
: SchemaReferenceType::Dynamic};
const sourcemeta::core::URI anchor_uri{anchor_uri_string};
auto recursive_ref_pointer{common_pointer_weak};
recursive_ref_pointer.push_back(std::cref(KEYWORD_RECURSIVE_REF));
const auto [it, inserted] = this->references_.insert_or_assign(
{reference_type, common_pointer.concat({"$recursiveRef"})},
{reference_type, std::move(recursive_ref_pointer)},
SchemaFrame::ReferencesEntry{.original = ref,
.destination = anchor_uri.recompose(),
.base = std::string_view{},
Expand Down Expand Up @@ -896,10 +907,12 @@ auto SchemaFrame::analyse(const JSON &root, const SchemaWalker &walker,
!has_fragment ||
(has_fragment && maybe_static_frame != this->locations_.end() &&
maybe_dynamic_frame == this->locations_.end())};
auto dynamic_ref_pointer{common_pointer_weak};
dynamic_ref_pointer.push_back(std::cref(KEYWORD_DYNAMIC_REF));
const auto [it, inserted] = this->references_.insert_or_assign(
{behaves_as_static ? SchemaReferenceType::Static
: SchemaReferenceType::Dynamic,
common_pointer.concat({"$dynamicRef"})},
std::move(dynamic_ref_pointer)},
SchemaFrame::ReferencesEntry{.original = original,
.destination = std::move(ref_string),
.base = std::string_view{},
Expand Down Expand Up @@ -981,7 +994,7 @@ auto SchemaFrame::references() const noexcept -> const References & {
auto SchemaFrame::reference(const SchemaReferenceType type,
const WeakPointer &pointer) const
-> std::optional<std::reference_wrapper<const ReferencesEntry>> {
const auto result{this->references_.find({type, to_pointer(pointer)})};
const auto result{this->references_.find({type, pointer})};
if (result != this->references_.cend()) {
return result->second;
}
Expand Down Expand Up @@ -1086,7 +1099,7 @@ auto SchemaFrame::dereference(const Location &location,
-> std::pair<SchemaReferenceType,
std::optional<std::reference_wrapper<const Location>>> {
const auto effective_location{
to_pointer(location.pointer.concat(relative_schema_location))};
location.pointer.concat(relative_schema_location)};
const auto maybe_reference_entry{this->references_.find(
{SchemaReferenceType::Static, effective_location})};
if (maybe_reference_entry == this->references_.cend()) {
Expand Down Expand Up @@ -1118,7 +1131,7 @@ auto SchemaFrame::for_each_resource_uri(
}

auto SchemaFrame::for_each_unresolved_reference(
const std::function<void(const Pointer &, const ReferencesEntry &)>
const std::function<void(const WeakPointer &, const ReferencesEntry &)>
&callback) const -> void {
for (const auto &[key, reference] : this->references_) {
if (!this->traverse(reference.destination).has_value()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ namespace sourcemeta::core {
/// - Pointer (reference keyword from the origin)
/// - Target URI
/// - Target schema
using DependencyCallback = std::function<void(std::string_view, const Pointer &,
std::string_view, const JSON &)>;
using DependencyCallback = std::function<void(
std::string_view, const WeakPointer &, std::string_view, const JSON &)>;

/// @ingroup jsonschema
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,8 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
/// The reference type is part of the key as it is possible to
/// have a static and a dynamic reference to the same location
/// on the same schema object.
using References = std::map<std::pair<SchemaReferenceType,
// TODO: Turn this into a weak pointer
// or reference to the location pointer?
Pointer>,
ReferencesEntry>;
using References =
std::map<std::pair<SchemaReferenceType, WeakPointer>, ReferencesEntry>;

#if defined(__GNUC__)
#pragma GCC diagnostic push
Expand Down Expand Up @@ -204,7 +201,7 @@ class SOURCEMETA_CORE_JSONSCHEMA_EXPORT SchemaFrame {
/// Iterate over all unresolved references (where destination cannot be
/// traversed)
auto for_each_unresolved_reference(
const std::function<void(const Pointer &, const ReferencesEntry &)>
const std::function<void(const WeakPointer &, const ReferencesEntry &)>
&callback) const -> void;

/// Check if there are any references to a given location pointer
Expand Down
3 changes: 2 additions & 1 deletion src/core/jsonschema/transformer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ auto SchemaTransformer::apply(JSON &schema, const SchemaWalker &walker,

const auto &target{destination.value().get()};
potentially_broken_references.push_back(
{reference.first.second, JSON::String{reference.second.original},
{to_pointer(reference.first.second),
JSON::String{reference.second.original},
reference.second.destination, to_pointer(target.pointer),
target.relative_pointer});
}
Expand Down
10 changes: 4 additions & 6 deletions src/extension/alterschema/common/orphan_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,15 @@ class OrphanDefinitions final : public SchemaTransformRule {
const auto destination_location{frame.traverse(reference.destination)};
if (destination_location.has_value()) {
const auto &destination_pointer{destination_location->get().pointer};
const auto source_pointer{to_weak_pointer(key.second)};
if (has_defs) {
process_reference(source_pointer, destination_pointer,
location.pointer, "$defs", has_external_to_defs,
process_reference(key.second, destination_pointer, location.pointer,
"$defs", has_external_to_defs,
outside_referenced_defs);
}

if (has_definitions) {
process_reference(source_pointer, destination_pointer,
location.pointer, "definitions",
has_external_to_definitions,
process_reference(key.second, destination_pointer, location.pointer,
"definitions", has_external_to_definitions,
outside_referenced_definitions);
}
}
Expand Down
20 changes: 11 additions & 9 deletions src/extension/editorschema/editorschema.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,26 +94,27 @@ auto for_editor(JSON &schema, const SchemaWalker &walker,
if (key.first == SchemaReferenceType::Dynamic) {
if (reference.fragment.has_value()) {
const auto destination{top_dynamic_anchor_location(
frame, to_weak_pointer(key.second), reference.fragment.value(),
frame, key.second, reference.fragment.value(),
reference.destination)};
if (!destination.has_value()) {
continue;
}

reference_changes.push_back(
{key.second, to_uri(destination.value().get()).recompose(),
keyword, true});
{to_pointer(key.second),
to_uri(destination.value().get()).recompose(), keyword, true});
} else {
reference_changes.push_back({key.second, "", keyword, true});
reference_changes.push_back(
{to_pointer(key.second), "", keyword, true});
}
} else {
if (keyword == "$schema") {
const auto uri{frame.uri(to_weak_pointer(key.second))};
const auto uri{frame.uri(key.second)};
assert(uri.has_value());
const auto origin{frame.traverse(uri.value().get())};
assert(origin.has_value());
reference_changes.push_back(
{key.second,
{to_pointer(key.second),
JSON::String{to_string(origin.value().get().base_dialect)},
keyword, false});
continue;
Expand All @@ -124,11 +125,12 @@ auto for_editor(JSON &schema, const SchemaWalker &walker,
const bool should_rename =
keyword == "$dynamicRef" || keyword == "$recursiveRef";
reference_changes.push_back(
{key.second, to_uri(result.value().get().pointer).recompose(),
keyword, should_rename});
{to_pointer(key.second),
to_uri(result.value().get().pointer).recompose(), keyword,
should_rename});
} else {
reference_changes.push_back(
{key.second, reference.destination, keyword, false});
{to_pointer(key.second), reference.destination, keyword, false});
}
}
}
Expand Down
Loading