diff --git a/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h b/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h index 0f7732a42..07f63d51b 100644 --- a/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h +++ b/src/core/jsonpointer/include/sourcemeta/core/jsonpointer_template.h @@ -20,10 +20,14 @@ template class GenericPointerTemplate { auto operator==(const Condition &) const noexcept -> bool = default; auto operator<(const Condition &) const noexcept -> bool { return false; } }; + struct Negation { + auto operator==(const Negation &) const noexcept -> bool = default; + auto operator<(const Negation &) const noexcept -> bool { return false; } + }; using Regex = typename PointerT::Value::String; using Token = typename PointerT::Token; using Container = - std::vector>; + std::vector>; /// This constructor creates an empty JSON Pointer template. For example: /// diff --git a/src/core/jsonpointer/stringify.h b/src/core/jsonpointer/stringify.h index d2171d748..92d5dfef5 100644 --- a/src/core/jsonpointer/stringify.h +++ b/src/core/jsonpointer/stringify.h @@ -474,6 +474,11 @@ auto stringify(const PointerT &pointer, stream.put(internal::token_pointer_tilde); stream.put('?'); stream.put(internal::token_pointer_tilde); + } else if (std::holds_alternative(token)) { + stream.put(internal::token_pointer_slash); + stream.put(internal::token_pointer_tilde); + stream.put('!'); + stream.put(internal::token_pointer_tilde); } else { stringify_token( std::get(token), stream, diff --git a/src/core/jsonschema/walker.cc b/src/core/jsonschema/walker.cc index a9021c289..2ab5f21b5 100644 --- a/src/core/jsonschema/walker.cc +++ b/src/core/jsonschema/walker.cc @@ -130,7 +130,11 @@ auto walk(const std::optional &parent, case sourcemeta::core::SchemaKeywordType::ApplicatorValueInPlaceNegate: { sourcemeta::core::Pointer new_pointer{pointer}; new_pointer.emplace_back(pair.first); - walk(pointer, new_pointer, instance_location, {}, subschemas, + auto new_instance_location{instance_location}; + new_instance_location.emplace_back( + sourcemeta::core::PointerTemplate::Negation{}); + walk(pointer, new_pointer, new_instance_location, + {sourcemeta::core::PointerTemplate::Negation{}}, subschemas, pair.second, walker, resolver, new_dialect, type, level + 1, orphan); } break; @@ -204,11 +208,14 @@ auto walk(const std::optional &parent, new_pointer.emplace_back(index); auto new_instance_location{instance_location}; new_instance_location.emplace_back( - sourcemeta::core::PointerTemplate::Conditional{}); + sourcemeta::core::PointerTemplate::Condition{}); + new_instance_location.emplace_back( + sourcemeta::core::PointerTemplate::Negation{}); walk(pointer, new_pointer, new_instance_location, - {sourcemeta::core::PointerTemplate::Conditional{}}, subschemas, - pair.second.at(index), walker, resolver, new_dialect, type, - level + 1, orphan); + {sourcemeta::core::PointerTemplate::Condition{}, + sourcemeta::core::PointerTemplate::Negation{}}, + subschemas, pair.second.at(index), walker, resolver, + new_dialect, type, level + 1, orphan); } } diff --git a/test/jsonpointer/jsonpointer_template_test.cc b/test/jsonpointer/jsonpointer_template_test.cc index fcb78a7c8..ad3f82cff 100644 --- a/test/jsonpointer/jsonpointer_template_test.cc +++ b/test/jsonpointer/jsonpointer_template_test.cc @@ -164,6 +164,36 @@ TEST(JSONPointer_template, equality_with_condition_wildcard_false) { EXPECT_NE(left, right); } +TEST(JSONPointer_template, equality_with_negation_wildcard_true) { + const sourcemeta::core::Pointer prefix{"foo", "bar"}; + const sourcemeta::core::Pointer suffix{"baz"}; + + sourcemeta::core::PointerTemplate left{prefix}; + left.emplace_back(sourcemeta::core::PointerTemplate::Negation{}); + left.push_back(suffix); + + sourcemeta::core::PointerTemplate right{prefix}; + right.emplace_back(sourcemeta::core::PointerTemplate::Negation{}); + right.push_back(suffix); + + EXPECT_EQ(left, right); +} + +TEST(JSONPointer_template, equality_with_negation_wildcard_false) { + const sourcemeta::core::Pointer prefix{"foo", "bar"}; + const sourcemeta::core::Pointer suffix{"baz"}; + + sourcemeta::core::PointerTemplate left{prefix}; + left.emplace_back(sourcemeta::core::PointerTemplate::Negation{}); + left.push_back(suffix); + + sourcemeta::core::PointerTemplate right{prefix}; + right.push_back(suffix); + right.emplace_back(sourcemeta::core::PointerTemplate::Negation{}); + + EXPECT_NE(left, right); +} + TEST(JSONPointer_template, pop_back) { const sourcemeta::core::Pointer base{"foo", "bar"}; sourcemeta::core::PointerTemplate pointer{base}; @@ -244,6 +274,18 @@ TEST(JSONPointer_template, stringify_condition) { EXPECT_EQ(stream.str(), "/foo/bar/~?~"); } +TEST(JSONPointer_template, stringify_negation) { + const sourcemeta::core::Pointer prefix{"foo", "bar"}; + + sourcemeta::core::PointerTemplate pointer{prefix}; + pointer.emplace_back(sourcemeta::core::PointerTemplate::Negation{}); + + std::ostringstream stream; + sourcemeta::core::stringify(pointer, stream); + + EXPECT_EQ(stream.str(), "/foo/bar/~!~"); +} + TEST(JSONPointer_template, concat_move) { const sourcemeta::core::Pointer pointer_left{"foo"}; const sourcemeta::core::Pointer pointer_right{"bar", "baz"}; diff --git a/test/jsonschema/jsonschema_official_walker_2019_09_test.cc b/test/jsonschema/jsonschema_official_walker_2019_09_test.cc index b419dea00..a4038fa2e 100644 --- a/test/jsonschema/jsonschema_official_walker_2019_09_test.cc +++ b/test/jsonschema/jsonschema_official_walker_2019_09_test.cc @@ -1555,7 +1555,7 @@ TEST(JSONSchema_official_walker_2019_09, instance_locations) { EXPECT_OFFICIAL_WALKER_ENTRY_2019_09(entries, 5, "/if", "", "/~?~", "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_2019_09(entries, 6, "/then", "", "/~?~", "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_2019_09(entries, 7, "/else", "", "/~?~", "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_2019_09(entries, 8, "/not", "", "", ""); + EXPECT_OFFICIAL_WALKER_ENTRY_2019_09(entries, 8, "/not", "", "/~!~", "/~!~"); // Applicator vocabulary (object) EXPECT_OFFICIAL_WALKER_ENTRY_2019_09(entries, 9, "/properties/foo", "", diff --git a/test/jsonschema/jsonschema_official_walker_2020_12_test.cc b/test/jsonschema/jsonschema_official_walker_2020_12_test.cc index 4e6a618b4..b0b2afa8b 100644 --- a/test/jsonschema/jsonschema_official_walker_2020_12_test.cc +++ b/test/jsonschema/jsonschema_official_walker_2020_12_test.cc @@ -1632,7 +1632,7 @@ TEST(JSONSchema_official_walker_2020_12, instance_locations) { EXPECT_OFFICIAL_WALKER_ENTRY_2020_12(entries, 5, "/if", "", "/~?~", "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_2020_12(entries, 6, "/then", "", "/~?~", "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_2020_12(entries, 7, "/else", "", "/~?~", "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_2020_12(entries, 8, "/not", "", "", ""); + EXPECT_OFFICIAL_WALKER_ENTRY_2020_12(entries, 8, "/not", "", "/~!~", "/~!~"); // Applicator vocabulary (object) EXPECT_OFFICIAL_WALKER_ENTRY_2020_12(entries, 9, "/properties/foo", "", diff --git a/test/jsonschema/jsonschema_official_walker_draft3_test.cc b/test/jsonschema/jsonschema_official_walker_draft3_test.cc index 8494cbe91..5d8ece003 100644 --- a/test/jsonschema/jsonschema_official_walker_draft3_test.cc +++ b/test/jsonschema/jsonschema_official_walker_draft3_test.cc @@ -677,10 +677,10 @@ TEST(JSONSchema_official_walker_draft3, instance_locations) { "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 12, "/type/2", "", "/~?~", "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 13, "/disallow/1", "", "/~?~", - "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 14, "/disallow/2", "", "/~?~", - "/~?~"); + EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 13, "/disallow/1", "", + "/~?~/~!~", "/~?~/~!~"); + EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 14, "/disallow/2", "", + "/~?~/~!~", "/~?~/~!~"); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 15, "/extends", "", "", ""); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT3(entries, 16, "/extends/extends/0", "/extends", "", ""); diff --git a/test/jsonschema/jsonschema_official_walker_draft4_test.cc b/test/jsonschema/jsonschema_official_walker_draft4_test.cc index 1aa2f7a3e..7c2b1d37f 100644 --- a/test/jsonschema/jsonschema_official_walker_draft4_test.cc +++ b/test/jsonschema/jsonschema_official_walker_draft4_test.cc @@ -683,7 +683,7 @@ TEST(JSONSchema_official_walker_draft4, instance_locations) { "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT4(entries, 4, "/oneOf/0", "", "/~?~", "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT4(entries, 5, "/not", "", "", ""); + EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT4(entries, 5, "/not", "", "/~!~", "/~!~"); // Applicators (object) EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT4(entries, 6, "/properties/foo", "", "/foo", diff --git a/test/jsonschema/jsonschema_official_walker_draft6_test.cc b/test/jsonschema/jsonschema_official_walker_draft6_test.cc index 0843fb82e..74401879f 100644 --- a/test/jsonschema/jsonschema_official_walker_draft6_test.cc +++ b/test/jsonschema/jsonschema_official_walker_draft6_test.cc @@ -737,7 +737,7 @@ TEST(JSONSchema_official_walker_draft6, instance_locations) { "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT6(entries, 4, "/oneOf/0", "", "/~?~", "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT6(entries, 5, "/not", "", "", ""); + EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT6(entries, 5, "/not", "", "/~!~", "/~!~"); // Applicators (object) EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT6(entries, 6, "/properties/foo", "", "/foo", diff --git a/test/jsonschema/jsonschema_official_walker_draft7_test.cc b/test/jsonschema/jsonschema_official_walker_draft7_test.cc index 0c78168fa..af74d7fa4 100644 --- a/test/jsonschema/jsonschema_official_walker_draft7_test.cc +++ b/test/jsonschema/jsonschema_official_walker_draft7_test.cc @@ -925,7 +925,7 @@ TEST(JSONSchema_official_walker_draft7, instance_locations) { EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT7(entries, 5, "/if", "", "/~?~", "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT7(entries, 6, "/then", "", "/~?~", "/~?~"); EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT7(entries, 7, "/else", "", "/~?~", "/~?~"); - EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT7(entries, 8, "/not", "", "", ""); + EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT7(entries, 8, "/not", "", "/~!~", "/~!~"); // Applicators (object) EXPECT_OFFICIAL_WALKER_ENTRY_DRAFT7(entries, 9, "/properties/foo", "", "/foo",