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 7557fb2..97e709f 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 @@ -26,6 +26,7 @@ import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc; import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc2673; import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc3339; +import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc4291; import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc5321; import java.util.List; import java.util.Map; @@ -46,7 +47,8 @@ final class Formats { .map(entry -> new RfcBasedFormat(entry.getKey(), new Rfc3339(), entry.getValue())), Stream.of( new RfcBasedFormat("email", new Rfc5321(), "mailbox"), - new RfcBasedFormat("ipv4", new Rfc2673(), "dotted-quad") + new RfcBasedFormat("ipv4", new Rfc2673(), "dotted-quad"), + new RfcBasedFormat("ipv6", new Rfc4291(), "address") ) ) .map(Format.class::cast) diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfc4291.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfc4291.java new file mode 100644 index 0000000..88e6853 --- /dev/null +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/rfc/Rfc4291.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Copyright 2024 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc; + +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; + +public final class Rfc4291 implements Rfc { + + private static final Map RULES = Map.of( + "address", + Pattern.compile( + "^(([0-9A-Fa-f]{1,4}(\\:[0-9A-Fa-f]{1,4}){7})|" + + "([0-9A-Fa-f]{1,4}(\\:[0-9A-Fa-f]{1,4}){0,5})*\\:\\:([0-9A-Fa-f]{1,4}(\\:[0-9A-Fa-f]{1,4}){0,5})*|" + + "(((([0-9A-Fa-f]{1,4}(\\:[0-9A-Fa-f]{1,4}){5})\\:)|" + + "([0-9A-Fa-f]{1,4}(\\:[0-9A-Fa-f]{1,4}){0,4})*\\:\\:([0-9A-Fa-f]{1,4}(\\:[0-9A-Fa-f]{1,4}){0,4})*)" + + "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))$" + ) + ); + + @Override + public Optional findRuleByName(final String ruleName) { + return Optional.ofNullable(RULES.get(ruleName)).map(RegExRule::new); + } +} diff --git a/vocabulary/format-assertion/src/main/resources/rfc/rfc4291 b/vocabulary/format-assertion/src/main/resources/rfc/rfc4291 new file mode 100644 index 0000000..e561c59 --- /dev/null +++ b/vocabulary/format-assertion/src/main/resources/rfc/rfc4291 @@ -0,0 +1,7 @@ +; selfmade :( -> not effectiv +dotted-quad = decbyte "." decbyte "." decbyte "." decbyte +decbyte = 1*3DIGIT +address = (hexbytes 7(":" hexbytes) ) / *(hexbytes *5(":" hexbytes)) zeros *(hexbytes *5(":" hexbytes)) / ((((hexbytes 5(":" hexbytes) ) ":") / *(hexbytes *4(":" hexbytes)) zeros *(hexbytes *4(":" hexbytes))) dotted-quad) +zeros = "::" +hexbytes = 1*4hexValue +hexValue = DIGIT / %x41-46 / %x61-66 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 77e62a2..0f299e3 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 @@ -74,4 +74,26 @@ void should_found_ipv4Format() { assertThat(new Formats().findByName("ipv4").applyTo("161.111.26.9"), is(true)); assertThat(new Formats().findByName("ipv4").applyTo("125.158.4589.1"), is(false)); } + + @Test + void should_found_ipv6Format() { + assertThat(new Formats().findByName("ipv6").applyTo("ABCD:EF01:2345:6789:ABCD:EF01:2345:6789"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("2001:DB8:0:0:8:800:200C:417A"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("FF01:0:0:0:0:0:0:101"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("0:0:0:0:0:0:0:1"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("0:0:0:0:0:0:0:0"), is(true)); + //compressed + assertThat(new Formats().findByName("ipv6").applyTo("2001:DB8::8:800:200C:417A"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("FF01::101"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("::1"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("::"), is(true)); + //ipv4 representation + assertThat(new Formats().findByName("ipv6").applyTo("0:0:0:0:0:0:13.1.68.3"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("0:0:0:0:0:FFFF:129.144.52.38"), is(true)); + //ipv4 representation compressed + assertThat(new Formats().findByName("ipv6").applyTo("::13.1.68.3"), is(true)); + assertThat(new Formats().findByName("ipv6").applyTo("::FFFF:129.144.52.38"), is(true)); + //ipv4 only -> invalid! + assertThat(new Formats().findByName("ipv6").applyTo("125.158.4589.1"), is(false)); + } }