From 666358a383798413a05cfefbfc21a639e1f7b410 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Mon, 9 Dec 2024 21:27:39 +0100 Subject: [PATCH] add support for relative-json-pointer format --- .../vocabulary/formatassertion/Formats.java | 3 +- .../vocabulary/formatassertion/rfc/Rfcs.java | 13 +++ .../draft-handrews-relative-json-pointer-01 | 12 +++ .../src/main/resources/rfc/rfc3987 | 97 +++++++++++++++++++ .../formatassertion/FormatsTest.java | 12 ++- 5 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 vocabulary/format-assertion/src/main/resources/rfc/draft-handrews-relative-json-pointer-01 create mode 100644 vocabulary/format-assertion/src/main/resources/rfc/rfc3987 diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Formats.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Formats.java index 19b744e..204005c 100644 --- a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Formats.java +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Formats.java @@ -46,7 +46,8 @@ final class Formats { Map.entry("ipv6", Map.entry(4291, "IPv6address")), Map.entry("json-pointer", Map.entry(6901, "json-pointer")), Map.entry("iri", Map.entry(3987, "IRI")), - Map.entry("iri-reference", Map.entry(3987, "IRI-reference")) + Map.entry("iri-reference", Map.entry(3987, "IRI-reference")), + Map.entry("relative-json-pointer", Map.entry(20180723, "relative-json-pointer")) ); Format findByName(final String name) { diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfcs.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfcs.java index 6cd1b69..1fed857 100644 --- a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfcs.java +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfcs.java @@ -424,6 +424,19 @@ public final class Rfcs { Map.entry( 6901, Map.of("json-pointer", new RegExRule(Pattern.compile("^(/([\\x00-.0-}\\x7f-\\u010ffff]|\\~[01])*)*$"))) + ), + //draft -> use expired date as number :) + Map.entry( + 20180723, + Map.of( + "relative-json-pointer", + new RegExRule( + Pattern.compile( + "^(0|[1-9][0-9]*)((/([\\x00-.0-}\\x7f-\\u010ffff]|\\~[01])*)*|#)$", + Pattern.CASE_INSENSITIVE + ) + ) + ) ) ); diff --git a/vocabulary/format-assertion/src/main/resources/rfc/draft-handrews-relative-json-pointer-01 b/vocabulary/format-assertion/src/main/resources/rfc/draft-handrews-relative-json-pointer-01 new file mode 100644 index 0000000..7b6f630 --- /dev/null +++ b/vocabulary/format-assertion/src/main/resources/rfc/draft-handrews-relative-json-pointer-01 @@ -0,0 +1,12 @@ +relative-json-pointer = non-negative-integer json-pointer +relative-json-pointer =/ non-negative-integer "#" +non-negative-integer = %x30 / %x31-39 *( %x30-39 ) + ; "0", or digits without a leading "0" + +json-pointer = *( "/" reference-token ) +reference-token = *( unescaped / escaped ) +unescaped = %x00-2E / %x30-7D / %x7F-10FFFF + ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' +escaped = "~" ( "0" / "1" ) + ; representing '~' and '/', respectively + diff --git a/vocabulary/format-assertion/src/main/resources/rfc/rfc3987 b/vocabulary/format-assertion/src/main/resources/rfc/rfc3987 new file mode 100644 index 0000000..0da79a4 --- /dev/null +++ b/vocabulary/format-assertion/src/main/resources/rfc/rfc3987 @@ -0,0 +1,97 @@ +IRI = scheme ":" ihier-part [ "?" iquery ] + [ "#" ifragment ] + +ihier-part = "//" iauthority ipath-abempty + / ipath-absolute + / ipath-rootless + / ipath-empty + +IRI-reference = IRI / irelative-ref + +absolute-IRI = scheme ":" ihier-part [ "?" iquery ] + +irelative-ref = irelative-part [ "?" iquery ] [ "#" ifragment ] + +irelative-part = "//" iauthority ipath-abempty + / ipath-absolute + / ipath-noscheme + / ipath-empty + +iauthority = [ iuserinfo "@" ] ihost [ ":" port ] +iuserinfo = *( iunreserved / pct-encoded / sub-delims / ":" ) +ihost = IP-literal / IPv4address / ireg-name + +ireg-name = *( iunreserved / pct-encoded / sub-delims ) + +ipath = ipath-abempty ; begins with "/" or is empty + / ipath-absolute ; begins with "/" but not "//" + / ipath-noscheme ; begins with a non-colon segment + / ipath-rootless ; begins with a segment + / ipath-empty ; zero characters + +ipath-abempty = *( "/" isegment ) +ipath-absolute = "/" [ isegment-nz *( "/" isegment ) ] +ipath-noscheme = isegment-nz-nc *( "/" isegment ) +ipath-rootless = isegment-nz *( "/" isegment ) +ipath-empty = 0 + +isegment = *ipchar +isegment-nz = 1*ipchar +isegment-nz-nc = 1*( iunreserved / pct-encoded / sub-delims + / "@" ) + ; non-zero-length segment without any colon ":" + +ipchar = iunreserved / pct-encoded / sub-delims / ":" + / "@" + +iquery = *( ipchar / iprivate / "/" / "?" ) + +ifragment = *( ipchar / "/" / "?" ) + +iunreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar + +ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF + / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD + / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD + / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD + / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD + / %xD0000-DFFFD / %xE1000-EFFFD + +iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD + +scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + +port = *DIGIT + +IP-literal = "[" ( IPv6address / IPvFuture ) "]" + +IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + +IPv6address = 6( h16 ":" ) ls32 + / "::" 5( h16 ":" ) ls32 + / [ h16 ] "::" 4( h16 ":" ) ls32 + / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + / [ *4( h16 ":" ) h16 ] "::" ls32 + / [ *5( h16 ":" ) h16 ] "::" h16 + / [ *6( h16 ":" ) h16 ] "::" + +h16 = 1*4HEXDIG +ls32 = ( h16 ":" h16 ) / IPv4address + +IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + +dec-octet = DIGIT ; 0-9 + / %x31-39 DIGIT ; 10-99 + / "1" 2DIGIT ; 100-199 + / "2" %x30-34 DIGIT ; 200-249 + / "25" %x30-35 ; 250-255 + +pct-encoded = "%" HEXDIG HEXDIG + +unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" +reserved = gen-delims / sub-delims +gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" +sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" \ No newline at end of file diff --git a/vocabulary/format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatsTest.java b/vocabulary/format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatsTest.java index 3e10701..cc4d622 100644 --- a/vocabulary/format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatsTest.java +++ b/vocabulary/format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatsTest.java @@ -133,7 +133,7 @@ void should_found_uuidformat() { @Test void should_found_jsonpointerformat() { assertThat(new Formats().findByName("json-pointer").applyTo("/abc"), is(true)); - assertThat(new Formats().findByName("json-pointer:").applyTo("abc"), is(false)); + assertThat(new Formats().findByName("json-pointer").applyTo("abc"), is(false)); } @Test @@ -157,4 +157,14 @@ void should_found_irireferenceformat() { assertThat(new Formats().findByName("iri-reference").applyTo("http://www.myfictionαlbank.com"), is(true)); assertThat(new Formats().findByName("iri-reference").applyTo("1://noIri"), is(false)); } + + @Test + void should_found_relativejsonpointerformat() { + assertThat(new Formats().findByName("relative-json-pointer").applyTo("0"), is(true)); + assertThat(new Formats().findByName("relative-json-pointer").applyTo("1/0"), is(true)); + assertThat(new Formats().findByName("relative-json-pointer").applyTo("2/highly/nested/objects"), is(true)); + assertThat(new Formats().findByName("relative-json-pointer").applyTo("0#"), is(true)); + assertThat(new Formats().findByName("relative-json-pointer").applyTo("1#"), is(true)); + assertThat(new Formats().findByName("relative-json-pointer").applyTo("nopointer"), is(false)); + } }