diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Format.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Format.java new file mode 100644 index 0000000..1434fd2 --- /dev/null +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/Format.java @@ -0,0 +1,30 @@ +/* + * 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; + +public interface Format { + boolean applyTo(String value); + + String name(); +} diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatKeyword.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatKeyword.java index 2c2a601..a7e54af 100644 --- a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatKeyword.java +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/FormatKeyword.java @@ -37,9 +37,9 @@ final class FormatKeyword implements Assertion, Annotation { static final String NAME = "format"; - private final Formats.Format format; + private final Format format; - FormatKeyword(final Formats.Format format) { + FormatKeyword(final Format format) { this.format = Objects.requireNonNull(format); } 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 28b7890..242df9f 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 @@ -23,14 +23,14 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion; -import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc; import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfcs; import java.util.Map; -import java.util.Objects; import java.util.Optional; final class Formats { + private static final Map CUSTOM_FORMAT = Map.of("regex", new RegExFormat()); + private static final Map> RFCS = Map.ofEntries( Map.entry("hostname", Map.entry(1123, "hostname")), Map.entry("date-time", Map.entry(3339, "date-time")), @@ -49,60 +49,12 @@ final class Formats { Format findByName(final String name) { return Optional.ofNullable(RFCS.get(name)) - .stream() - .map(entry -> + .flatMap(entry -> Rfcs.findRfcByNumber(entry.getKey()).map(rfc -> new RfcBasedFormat(name, rfc, entry.getValue())) ) - .flatMap(Optional::stream) .map(Format.class::cast) - .findFirst() + .or(() -> Optional.ofNullable(CUSTOM_FORMAT.get(name))) + .map(Format.class::cast) .orElseGet(() -> new UnknownFormat(name)); } - - private static class RfcBasedFormat implements Format { - - private final String formatName; - private final Rfc rfc; - private final String ruleName; - - public RfcBasedFormat(final String formatName, final Rfc rfc, final String ruleName) { - this.formatName = Objects.requireNonNull(formatName); - this.rfc = Objects.requireNonNull(rfc); - this.ruleName = Objects.requireNonNull(ruleName); - } - - @Override - public boolean applyTo(final String value) { - return rfc.findRuleByName(ruleName).map(r -> r.applyTo(value)).orElse(Boolean.FALSE); - } - - @Override - public String name() { - return formatName; - } - } - - interface Format { - boolean applyTo(String value); - String name(); - } - - private static class UnknownFormat implements Format { - - private final String name; - - public UnknownFormat(final String name) { - this.name = name; - } - - @Override - public String name() { - return name; - } - - @Override - public boolean applyTo(final String value) { - return false; - } - } } diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/RegExFormat.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/RegExFormat.java new file mode 100644 index 0000000..11a3219 --- /dev/null +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/RegExFormat.java @@ -0,0 +1,49 @@ +/* + * 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; + +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +final class RegExFormat implements Format { + + private static final Logger LOG = Logger.getLogger(RegExFormat.class.getName()); + + @Override + public boolean applyTo(final String value) { + try { + Pattern.compile(value); + return true; + } catch (Exception e) { + LOG.log(Level.FINE, "invalid regex!", e); + return false; + } + } + + @Override + public String name() { + return "regex"; + } +} diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/RfcBasedFormat.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/RfcBasedFormat.java new file mode 100644 index 0000000..d209b40 --- /dev/null +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/RfcBasedFormat.java @@ -0,0 +1,50 @@ +/* + * 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; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc; +import java.util.Objects; + +public final class RfcBasedFormat implements Format { + + private final String formatName; + private final Rfc rfc; + private final String ruleName; + + public RfcBasedFormat(final String formatName, final Rfc rfc, final String ruleName) { + this.formatName = Objects.requireNonNull(formatName); + this.rfc = Objects.requireNonNull(rfc); + this.ruleName = Objects.requireNonNull(ruleName); + } + + @Override + public boolean applyTo(final String value) { + return rfc.findRuleByName(ruleName).map(r -> r.applyTo(value)).orElse(Boolean.FALSE); + } + + @Override + public String name() { + return formatName; + } +} diff --git a/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/UnknownFormat.java b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/UnknownFormat.java new file mode 100644 index 0000000..a7a3aca --- /dev/null +++ b/vocabulary/format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/formatassertion/UnknownFormat.java @@ -0,0 +1,31 @@ +/* + * 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; + +public record UnknownFormat(String name) implements Format { + @Override + public boolean applyTo(final String value) { + return false; + } +} diff --git a/vocabulary/format-assertion/src/main/java/module-info.java b/vocabulary/format-assertion/src/main/java/module-info.java index fd4d7c0..b9cd1f4 100644 --- a/vocabulary/format-assertion/src/main/java/module-info.java +++ b/vocabulary/format-assertion/src/main/java/module-info.java @@ -25,6 +25,7 @@ requires io.github.sebastiantoepfer.jsonschema; requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; requires io.github.sebastiantoepfer.ddd.common; + requires java.logging; requires com.github.spotbugs.annotations; requires jakarta.json; 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 fb2dc1c..731f6f2 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 @@ -135,4 +135,11 @@ void should_found_jsonpointerformat() { assertThat(new Formats().findByName("json-pointer").applyTo("/abc"), is(true)); assertThat(new Formats().findByName("json-pointer:").applyTo("abc"), is(false)); } + + @Test + void should_found_regexformat() { + assertThat(new Formats().findByName("regex").name(), is("regex")); + assertThat(new Formats().findByName("regex").applyTo("^[0-9]*"), is(true)); + assertThat(new Formats().findByName("regex").applyTo("({"), is(false)); + } } diff --git a/vocabulary/format-assertion/src/test/java/module-info.java b/vocabulary/format-assertion/src/test/java/module-info.java index e4e0dc4..e839288 100644 --- a/vocabulary/format-assertion/src/test/java/module-info.java +++ b/vocabulary/format-assertion/src/test/java/module-info.java @@ -26,6 +26,7 @@ requires io.github.sebastiantoepfer.jsonschema; requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; requires io.github.sebastiantoepfer.ddd.common; + requires java.logging; requires jakarta.json; requires org.junit.jupiter.api;