From c39b3f6bf1bbcb213ffde3949b7cec16f894a08e Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:42:04 +0100 Subject: [PATCH 01/11] add module to support format assertion --- pom.xml | 3 +- vocabulary-format-assertion/pom.xml | 71 +++++++++++ .../assertion/DefaultFormatAssertion.java | 48 ++++++++ .../format/assertion/FormatAssertion.java | 30 +++++ .../assertion/FormatAssertionKeywordType.java | 98 +++++++++++++++ .../assertion/FormatAssertionVocabulary.java | 63 ++++++++++ .../src/main/java/module-info.java | 31 +++++ ...jsonschema.vocabulary.spi.LazyVocabularies | 1 + .../assertion/DefaultFormatAssertionTest.java | 42 +++++++ .../FormatAssertionKeywordTypeTest.java | 112 ++++++++++++++++++ .../FormatAssertionVocabularyTest.java | 101 ++++++++++++++++ 11 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 vocabulary-format-assertion/pom.xml create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java create mode 100644 vocabulary-format-assertion/src/main/java/module-info.java create mode 100644 vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java diff --git a/pom.xml b/pom.xml index aa0ec438..cdffb201 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,8 @@ api vocabulary-spi - core + core + vocabulary-format-assertion diff --git a/vocabulary-format-assertion/pom.xml b/vocabulary-format-assertion/pom.xml new file mode 100644 index 00000000..9b8e321f --- /dev/null +++ b/vocabulary-format-assertion/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + + io.github.sebastian-toepfer.json-schema + json-schema + 0.1.0-SNAPSHOT + + + json-schema-vocabulary-format-assertion + Json Schema :: vocabulary :: format assertion + + + + ${project.groupId} + json-schema-vocabulary-spi + ${project.version} + + + + org.junit.jupiter + junit-jupiter-api + test + 5.6.0 + + + org.junit.jupiter + junit-jupiter-engine + test + 5.6.0 + + + org.hamcrest + hamcrest + test + + + com.github.npathai + hamcrest-optional + test + + + ${project.groupId} + json-schema-core + ${project.version} + test + + + + jakarta.json + jakarta.json-api + provided + + + + org.eclipse.parsson + parsson + test + + + org.junit.jupiter + junit-jupiter-params + 5.6.0 + test + + + diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java new file mode 100644 index 00000000..d06b58b2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +import java.util.Objects; +import java.util.function.Predicate; + +public final class DefaultFormatAssertion implements FormatAssertion { + + private final String name; + private final Predicate predicate; + + public DefaultFormatAssertion(final String name, final Predicate predicate) { + this.name = Objects.requireNonNull(name); + this.predicate = Objects.requireNonNull(predicate); + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isValidFor(final String value) { + return predicate.test(value); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java new file mode 100644 index 00000000..76e9dbd0 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java @@ -0,0 +1,30 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +public interface FormatAssertion { + String name(); + + boolean isValidFor(String value); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java new file mode 100644 index 00000000..435dd4e9 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java @@ -0,0 +1,98 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +import io.github.sebastiantoepfer.jsonschema.InstanceType; +import io.github.sebastiantoepfer.jsonschema.JsonSchema; +import io.github.sebastiantoepfer.jsonschema.keyword.Annotation; +import io.github.sebastiantoepfer.jsonschema.keyword.Assertion; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.Json; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class FormatAssertionKeywordType implements KeywordType { + + private final List formats; + + public FormatAssertionKeywordType(final List formats) { + this.formats = List.copyOf(formats); + } + + @Override + public String name() { + return "format"; + } + + @Override + public Keyword createKeyword(final JsonSchema schema, final JsonValue value) { + if (InstanceType.STRING.isInstance(value)) { + return createKeyword(((JsonString) value).getString()); + } else { + throw new IllegalArgumentException("Value must be a string!"); + } + } + + private FormatAssertionKeyword createKeyword(final String formatName) { + return formats + .stream() + .filter(format -> format.name().equals(formatName)) + .map(FormatAssertionKeyword::new) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("format %s not supported!", formatName))); + } + + private class FormatAssertionKeyword implements Annotation, Assertion { + + private final FormatAssertion format; + + public FormatAssertionKeyword(final FormatAssertion format) { + this.format = format; + } + + @Override + public Collection categories() { + return List.of(KeywordCategory.ANNOTATION, KeywordCategory.ASSERTION); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public JsonValue valueFor(final JsonValue value) { + return Json.createValue(format.name()); + } + + @Override + public boolean isValidFor(final JsonValue instance) { + return !InstanceType.STRING.isInstance(instance) || format.isValidFor(((JsonString) instance).getString()); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java new file mode 100644 index 00000000..c76300ef --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +import io.github.sebastiantoepfer.jsonschema.Vocabulary; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies; +import java.net.URI; +import java.util.List; +import java.util.Optional; + +public class FormatAssertionVocabulary implements LazyVocabularies, Vocabulary { + + private final List keywords; + + public FormatAssertionVocabulary() { + this.keywords = + List.of(new FormatAssertionKeywordType(List.of(new DefaultFormatAssertion("email", s -> s.contains("@"))))); + } + + @Override + public Optional loadVocabularyWithId(final URI id) { + final Optional result; + if (id().equals(id)) { + result = Optional.of(this); + } else { + result = Optional.empty(); + } + return result; + } + + @Override + public URI id() { + return URI.create("https://json-schema.org/draft/2020-12/vocab/format-assertion"); + } + + @Override + public Optional findKeywordTypeByName(final String name) { + return keywords.stream().filter(k -> k.hasName(name)).findFirst(); + } +} diff --git a/vocabulary-format-assertion/src/main/java/module-info.java b/vocabulary-format-assertion/src/main/java/module-info.java new file mode 100644 index 00000000..66c685cd --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/module-info.java @@ -0,0 +1,31 @@ +/* + * The MIT License + * + * Copyright 2023 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. + */ +module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { + requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; + requires io.github.sebastiantoepfer.jsonschema; + requires jakarta.json; + + provides io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies + with io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.FormatAssertionVocabulary; +} diff --git a/vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies b/vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies new file mode 100644 index 00000000..809d22a5 --- /dev/null +++ b/vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies @@ -0,0 +1 @@ +io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.FormatAssertionVocabulary diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java new file mode 100644 index 00000000..088271a0 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +class DefaultFormatAssertionTest { + + @Test + void should_be_valid_if_predicate_match() { + assertThat(new DefaultFormatAssertion("test", s -> s.equals("test")).isValidFor("test"), is(true)); + } + + @Test + void should_be_invalid_if_predicate_not_match() { + assertThat(new DefaultFormatAssertion("test", s -> s.equals("test")).isValidFor("hallo"), is(false)); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java new file mode 100644 index 00000000..31e5de1b --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java @@ -0,0 +1,112 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.jsonschema.JsonSchema; +import io.github.sebastiantoepfer.jsonschema.JsonSchemas; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import java.util.List; +import org.junit.jupiter.api.Test; + +class FormatAssertionKeywordTypeTest { + + @Test + void should_know_his_name() { + final Keyword keyword = new FormatAssertionKeywordType(List.of(new TestFormat())) + .createKeyword(JsonSchemas.load(JsonValue.TRUE), Json.createValue("date")); + + assertThat(keyword.hasName("format"), is(true)); + assertThat(keyword.hasName("test"), is(false)); + } + + @Test + void should_not_be_createable_with_non_string() { + final JsonSchema schema = JsonSchemas.load(JsonValue.TRUE); + final KeywordType keywordType = new FormatAssertionKeywordType(List.of(new TestFormat())); + assertThrows( + IllegalArgumentException.class, + () -> keywordType.createKeyword(schema, JsonValue.EMPTY_JSON_OBJECT) + ); + } + + @Test + void should_not_be_createable_with_unknown_formatname() { + final JsonSchema schema = JsonSchemas.load(JsonValue.TRUE); + final KeywordType keywordType = new FormatAssertionKeywordType(List.of(new TestFormat())); + final JsonValue unknownFormatName = Json.createValue("test"); + assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(schema, unknownFormatName)); + } + + @Test + void should_be_assertion_and_annotation() { + assertThat( + new FormatAssertionKeywordType(List.of(new TestFormat())) + .createKeyword(JsonSchemas.load(JsonValue.TRUE), Json.createValue("date")) + .categories(), + containsInAnyOrder(Keyword.KeywordCategory.ASSERTION, Keyword.KeywordCategory.ANNOTATION) + ); + } + + @Test + void should_return_formatname_as_annotation() { + assertThat( + new FormatAssertionKeywordType(List.of(new TestFormat())) + .createKeyword(JsonSchemas.load(JsonValue.TRUE), Json.createValue("date")) + .asAnnotation() + .valueFor(JsonValue.EMPTY_JSON_OBJECT), + is(Json.createValue("date")) + ); + } + + private static final class TestFormat implements FormatAssertion { + + private final String name; + + public TestFormat() { + this("date"); + } + + public TestFormat(final String name) { + this.name = name; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isValidFor(final String value) { + return true; + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java new file mode 100644 index 00000000..1e6722a5 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion; + +import static com.github.npathai.hamcrestopt.OptionalMatchers.isEmpty; +import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent; +import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresentAnd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.JsonSchemas; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.Json; +import java.net.URI; +import org.junit.jupiter.api.Test; + +class FormatAssertionVocabularyTest { + + @Test + void should_be_return_empty_for_wrong_vocab_id() { + assertThat(new FormatAssertionVocabulary().loadVocabularyWithId(URI.create("http://localhost")), isEmpty()); + } + + @Test + void should_be_return_vocab_for_right_vocab_id() { + assertThat( + new FormatAssertionVocabulary() + .loadVocabularyWithId(URI.create("https://json-schema.org/draft/2020-12/vocab/format-assertion")), + isPresent() + ); + } + + @Test + void should_be_loaded_by_lazy_vocab_and_keyword_format_should_be_an_assertion_results_in_false() { + assertThat( + JsonSchemas + .load( + Json + .createObjectBuilder() + .add( + "$vocabulary", + Json + .createObjectBuilder() + .add("https://json-schema.org/draft/2020-12/vocab/format-assertion", true) + ) + .add("format", "email") + .build() + ) + .keywordByName("format") + .map(Keyword::asAssertion) + .map(assertion -> assertion.isValidFor(Json.createValue("test"))), + isPresentAnd(is(false)) + ); + } + + @Test + void should_be_loaded_by_lazy_vocab_and_keyword_format_should_be_an_assertion_results_in_true() { + assertThat( + JsonSchemas + .load( + Json + .createObjectBuilder() + .add( + "$vocabulary", + Json + .createObjectBuilder() + .add("https://json-schema.org/draft/2020-12/vocab/format-assertion", true) + ) + .add("format", "email") + .build() + ) + .keywordByName("format") + .map(Keyword::asAssertion) + .map(assertion -> + assertion.isValidFor(Json.createValue("61313468+sebastian-toepfer@users.noreply.github.com")) + ), + isPresentAnd(is(true)) + ); + } +} From fcf3a4c336dcf29d67578f4262dca9fb1ff88622 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Wed, 15 Nov 2023 10:30:02 +0100 Subject: [PATCH 02/11] add basic abnf --- vocabulary-format-assertion/pom.xml | 18 +- .../format/assertion/abnf/CoreRules.java | 165 +++++++++ .../format/assertion/abnf/Rule.java | 78 +++++ .../format/assertion/abnf/RuleList.java | 77 +++++ .../assertion/abnf/element/Alternative.java | 83 +++++ .../assertion/abnf/element/Concatenation.java | 83 +++++ .../assertion/abnf/element/Element.java | 29 ++ .../abnf/element/NumericCharacter.java | 108 ++++++ .../abnf/element/OptionalSequence.java | 77 +++++ .../assertion/abnf/element/RuleName.java | 67 ++++ .../assertion/abnf/element/RuleReference.java | 71 ++++ .../assertion/abnf/element/SequenceGroup.java | 87 +++++ .../abnf/element/SpecificRepetition.java | 77 +++++ .../assertion/abnf/element/StringElement.java | 78 +++++ .../abnf/element/ValueRangeAlternatives.java | 77 +++++ .../abnf/element/VariableRepetition.java | 108 ++++++ .../format/assertion/abnf/CoreRulesTest.java | 314 ++++++++++++++++++ .../format/assertion/abnf/RuleListTest.java | 47 +++ .../format/assertion/abnf/RuleTest.java | 48 +++ .../abnf/element/AlternativeTest.java | 50 +++ .../abnf/element/ConcatenationTest.java | 50 +++ .../abnf/element/NumericCharacterTest.java | 69 ++++ .../abnf/element/OptionalSequenceTest.java | 45 +++ .../assertion/abnf/element/RuleNameTest.java | 35 ++ .../abnf/element/RuleReferenceTest.java | 45 +++ .../abnf/element/SequenceGroupTest.java | 52 +++ .../abnf/element/SpecificRepetitionTest.java | 45 +++ .../abnf/element/StringElementTest.java | 67 ++++ .../element/ValueRangeAlternativesTest.java | 116 +++++++ .../abnf/element/VariableRepetitionTest.java | 63 ++++ .../src/test/java/module-info.java | 34 ++ 31 files changed, 2355 insertions(+), 8 deletions(-) create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java create mode 100644 vocabulary-format-assertion/src/test/java/module-info.java diff --git a/vocabulary-format-assertion/pom.xml b/vocabulary-format-assertion/pom.xml index 9b8e321f..f91f7494 100644 --- a/vocabulary-format-assertion/pom.xml +++ b/vocabulary-format-assertion/pom.xml @@ -25,13 +25,16 @@ org.junit.jupiter junit-jupiter-api test - 5.6.0 + + + org.junit.jupiter + junit-jupiter-params + test org.junit.jupiter junit-jupiter-engine test - 5.6.0 org.hamcrest @@ -43,6 +46,11 @@ hamcrest-optional test + + nl.jqno.equalsverifier + equalsverifier + test + ${project.groupId} json-schema-core @@ -61,11 +69,5 @@ parsson test - - org.junit.jupiter - junit-jupiter-params - 5.6.0 - test - diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java new file mode 100644 index 00000000..58a629ce --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java @@ -0,0 +1,165 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; + +public enum CoreRules implements Element { + ALPHA() { + @Override + public boolean isValidFor(final int codePoint) { + return Alternative + .of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x41), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x5A) + ), + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x61), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7A) + ) + ) + .isValidFor(codePoint); + } + }, + BIT() { + @Override + public boolean isValidFor(final int codePoint) { + return Alternative.of(StringElement.of("0"), StringElement.of("1")).isValidFor(codePoint); + } + }, + CHAR() { + @Override + public boolean isValidFor(final int codePoint) { + return ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x01), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7F) + ) + .isValidFor(codePoint); + } + }, + CR() { + @Override + public boolean isValidFor(final int codePoint) { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0D).isValidFor(codePoint); + } + }, + CRLF, //CR LF + CTL, //%x00-1F / %x7F + DIGIT() { + @Override + public boolean isValidFor(final int codePoint) { + return ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x30), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x39) + ) + .isValidFor(codePoint); + } + }, + DQUOTE() { + @Override + public boolean isValidFor(final int codePoint) { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x22).isValidFor(codePoint); + } + }, + HEXDIG() { + @Override + public boolean isValidFor(final int codePoint) { + return Alternative + .of( + DIGIT, + StringElement.of("A"), + StringElement.of("B"), + StringElement.of("C"), + StringElement.of("D"), + StringElement.of("E"), + StringElement.of("F") + ) + .isValidFor(codePoint); + } + }, + HTAB() { + @Override + public boolean isValidFor(final int codePoint) { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x09).isValidFor(codePoint); + } + }, + LF() { + @Override + public boolean isValidFor(final int codePoint) { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0A).isValidFor(codePoint); + } + }, + LWSP, //*(WSP / CRLF WSP) + OCTET() { + @Override + public boolean isValidFor(final int codePoint) { + return ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x00), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0xFF) + ) + .isValidFor(codePoint); + } + }, + SP() { + @Override + public boolean isValidFor(final int codePoint) { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20).isValidFor(codePoint); + } + }, + VCHAR() { + @Override + public boolean isValidFor(final int codePoint) { + return ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) + ) + .isValidFor(codePoint); + } + }, + WSP { + @Override + public boolean isValidFor(final int codePoint) { + return Alternative.of(SP, HTAB).isValidFor(codePoint); + } + }; + + public RuleName asRuleName() { + return RuleName.of(name()); + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java new file mode 100644 index 00000000..7645cba3 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import java.util.Objects; + +public final class Rule { + + public static Rule of(final RuleName name, final Element elements) { + return new Rule(name, elements); + } + + private final RuleName name; + private final Element elements; + + private Rule(final RuleName name, final Element elements) { + this.name = Objects.requireNonNull(name); + this.elements = Objects.requireNonNull(elements); + } + + public boolean hasRuleName(final RuleName name) { + return Objects.equals(this.name, name); + } + + @Override + public String toString() { + return "Rule{" + "name=" + name + ", elements=" + elements + '}'; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 83 * hash + Objects.hashCode(this.name); + hash = 83 * hash + Objects.hashCode(this.elements); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Rule other = (Rule) obj; + if (!Objects.equals(this.name, other.name)) { + return false; + } + return Objects.equals(this.elements, other.elements); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java new file mode 100644 index 00000000..10aa37ba --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java @@ -0,0 +1,77 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf; + +import static java.util.Arrays.asList; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class RuleList { + + public static RuleList of(final Rule rule, final Rule... rules) { + final List ruleList = new ArrayList<>(); + ruleList.add(Objects.requireNonNull(rule)); + ruleList.addAll(asList(rules)); + return of(ruleList); + } + + public static RuleList of(final List rules) { + return new RuleList(rules); + } + + private final List rules; + + private RuleList(final List rules) { + this.rules = List.copyOf(rules); + } + + @Override + public String toString() { + return "RuleList{" + "rules=" + rules + '}'; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 73 * hash + Objects.hashCode(this.rules); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final RuleList other = (RuleList) obj; + return Objects.equals(this.rules, other.rules); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java new file mode 100644 index 00000000..a29d79ba --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class Alternative implements Element { + + public static Alternative of(final Element left, final Element right, final Element... more) { + final List alternatives = new ArrayList<>(); + alternatives.add(left); + alternatives.add(right); + alternatives.addAll(Arrays.asList(more)); + return of(alternatives); + } + + public static Alternative of(final Collection alternatives) { + return new Alternative(alternatives); + } + + private final List alternatives; + + private Alternative(final Collection alternatives) { + this.alternatives = List.copyOf(alternatives); + } + + @Override + public boolean isValidFor(final int codePoint) { + return alternatives.stream().anyMatch(e -> e.isValidFor(codePoint)); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 41 * hash + Objects.hashCode(this.alternatives); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Alternative other = (Alternative) obj; + return Objects.equals(this.alternatives, other.alternatives); + } + + @Override + public String toString() { + return "Alternative{" + "alternatives=" + alternatives + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java new file mode 100644 index 00000000..9a2be703 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class Concatenation implements Element { + + public static Concatenation of(final Element left, final Element right, final Element... more) { + final List concatenations = new ArrayList<>(); + concatenations.add(left); + concatenations.add(right); + concatenations.addAll(Arrays.asList(more)); + return of(concatenations); + } + + public static Concatenation of(final Collection alternatives) { + return new Concatenation(alternatives); + } + + private final List concatenations; + + private Concatenation(final Collection concatenations) { + this.concatenations = List.copyOf(concatenations); + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + Objects.hashCode(this.concatenations); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Concatenation other = (Concatenation) obj; + return Objects.equals(this.concatenations, other.concatenations); + } + + @Override + public String toString() { + return "Concatenation{" + "concatenation=" + concatenations + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java new file mode 100644 index 00000000..ad3999c7 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java @@ -0,0 +1,29 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +public interface Element { + //no no, but let get start simple + boolean isValidFor(int codePoint); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java new file mode 100644 index 00000000..9db8237d --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java @@ -0,0 +1,108 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Arrays; +import java.util.Objects; + +public final class NumericCharacter implements Element { + + public static NumericCharacter of(final BASE base, final int value) { + return new NumericCharacter(base, value); + } + + private final BASE base; + private final int value; + + private NumericCharacter(final BASE base, final int value) { + this.base = Objects.requireNonNull(base); + this.value = value; + } + + @Override + public boolean isValidFor(final int codePoint) { + return value == codePoint; + } + + boolean lessThanOrEquals(final int codePoint) { + return value <= codePoint; + } + + boolean greatherThaneOrEquals(final int codePoint) { + return value >= codePoint; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + Objects.hashCode(value); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NumericCharacter other = (NumericCharacter) obj; + return value == other.value; + } + + @Override + public String toString() { + return "NumericCharacter{" + "base=" + base + ", value=" + value + '}'; + } + + public enum BASE { + BINARY('b', 2), + DECIMAL('d', 10), + HEXADECIMAL('x', 16); + + private final char baseShortName; + private final int radix; + + private BASE(final char baseShortName, final int base) { + this.baseShortName = baseShortName; + this.radix = base; + } + + public Integer convert(final String value) { + return Integer.valueOf(value, radix); + } + + public static BASE findByShortName(final char baseChar) { + return Arrays + .stream(BASE.values()) + .filter(b -> b.baseShortName == baseChar) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java new file mode 100644 index 00000000..86ece7b8 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java @@ -0,0 +1,77 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class OptionalSequence implements Element { + + public static OptionalSequence of(final Element optionalElement) { + return of(List.of(Objects.requireNonNull(optionalElement))); + } + + public static OptionalSequence of(final Collection optionalElements) { + return new OptionalSequence(optionalElements); + } + + private final List optionalElements; + + public OptionalSequence(final Collection optionalElements) { + this.optionalElements = List.copyOf(optionalElements); + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String toString() { + return "OptionalSequence{" + "optionalElements=" + optionalElements + '}'; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + Objects.hashCode(this.optionalElements); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OptionalSequence other = (OptionalSequence) obj; + return Objects.equals(this.optionalElements, other.optionalElements); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java new file mode 100644 index 00000000..6261da81 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Locale; +import java.util.Objects; + +public final class RuleName { + + public static RuleName of(final String name) { + return new RuleName(name); + } + + private final String name; + + private RuleName(final String name) { + this.name = Objects.requireNonNull(name); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + Objects.hashCode(name.toLowerCase(Locale.US)); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final RuleName other = (RuleName) obj; + return Objects.equals(this.name.toLowerCase(Locale.US), other.name.toLowerCase(Locale.US)); + } + + @Override + public String toString() { + return "RuleName{" + "name=" + name + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java new file mode 100644 index 00000000..0d7bb644 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java @@ -0,0 +1,71 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Objects; + +public final class RuleReference implements Element { + + public static RuleReference of(final RuleName name) { + return new RuleReference(name); + } + + private final RuleName name; + + private RuleReference(final RuleName name) { + this.name = name; + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String toString() { + return "RuleReference{" + "name=" + name + '}'; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 79 * hash + Objects.hashCode(this.name); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final RuleReference other = (RuleReference) obj; + return Objects.equals(this.name, other.name); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java new file mode 100644 index 00000000..c6dbff11 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java @@ -0,0 +1,87 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class SequenceGroup implements Element { + + public static SequenceGroup of(final Element term) { + return of(List.of(Objects.requireNonNull(term))); + } + + public static SequenceGroup of(final Element first, final Element second, final Element... more) { + final List elements = new ArrayList<>(); + elements.add(Objects.requireNonNull(first)); + elements.add(Objects.requireNonNull(second)); + elements.addAll(Arrays.asList(more)); + return of(elements); + } + + public static SequenceGroup of(final Collection elements) { + return new SequenceGroup(elements); + } + + private final List elements; + + private SequenceGroup(final Collection elements) { + this.elements = List.copyOf(elements); + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String toString() { + return "SequenceGroup{" + "elements=" + elements + '}'; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 17 * hash + Objects.hashCode(this.elements); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SequenceGroup other = (SequenceGroup) obj; + return Objects.equals(this.elements, other.elements); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java new file mode 100644 index 00000000..f6eaade4 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java @@ -0,0 +1,77 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Objects; + +public final class SpecificRepetition implements Element { + + public static SpecificRepetition of(final Element elementToRepeat, final int occurences) { + return new SpecificRepetition(elementToRepeat, occurences); + } + + private final Element elementToRepeat; + private final int occurences; + + private SpecificRepetition(final Element elementToRepeat, final int occurences) { + this.elementToRepeat = Objects.requireNonNull(elementToRepeat); + this.occurences = occurences; + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String toString() { + return "SpecificRepetition{" + "elementToRepeat=" + elementToRepeat + ", occurences=" + occurences + '}'; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 89 * hash + Objects.hashCode(this.elementToRepeat); + hash = 89 * hash + this.occurences; + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SpecificRepetition other = (SpecificRepetition) obj; + if (this.occurences != other.occurences) { + return false; + } + return Objects.equals(this.elementToRepeat, other.elementToRepeat); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java new file mode 100644 index 00000000..96c7c96f --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Locale; +import java.util.Objects; + +public final class StringElement implements Element { + + public static StringElement of(final String value) { + return new StringElement(value); + } + + private final String value; + + private StringElement(final String value) { + this.value = Objects.requireNonNull(value); + } + + @Override + public boolean isValidFor(final int codePoint) { + if (value.length() == 1) { + return ( + value.codePointAt(0) == Character.toUpperCase(codePoint) || + value.codePointAt(0) == Character.toLowerCase(codePoint) + ); + } + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 17 * hash + Objects.hashCode(this.value.toLowerCase(Locale.US)); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final StringElement other = (StringElement) obj; + return Objects.equals(this.value.toLowerCase(Locale.US), other.value.toLowerCase(Locale.US)); + } + + @Override + public String toString() { + return "StringElement{" + "value=" + value + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java new file mode 100644 index 00000000..39e14ab2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java @@ -0,0 +1,77 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Objects; + +public final class ValueRangeAlternatives implements Element { + + public static ValueRangeAlternatives of(final NumericCharacter start, final NumericCharacter end) { + return new ValueRangeAlternatives(start, end); + } + + private final NumericCharacter start; + private final NumericCharacter end; + + public ValueRangeAlternatives(final NumericCharacter start, final NumericCharacter end) { + this.start = start; + this.end = end; + } + + @Override + public boolean isValidFor(final int codePoint) { + return start.lessThanOrEquals(codePoint) && end.greatherThaneOrEquals(codePoint); + } + + @Override + public String toString() { + return "ValueRange{" + "start=" + start + ", end=" + end + '}'; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 89 * hash + Objects.hashCode(this.start); + hash = 89 * hash + Objects.hashCode(this.end); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ValueRangeAlternatives other = (ValueRangeAlternatives) obj; + if (!Objects.equals(this.start, other.start)) { + return false; + } + return Objects.equals(this.end, other.end); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java new file mode 100644 index 00000000..02acd3ed --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java @@ -0,0 +1,108 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.Objects; + +public final class VariableRepetition implements Element { + + public static VariableRepetition of(final Element element) { + return ofAtLeast(element, 0); + } + + public static VariableRepetition ofAtLeast(final Element element, final int minOccurrences) { + return ofBetween(element, minOccurrences, Integer.MAX_VALUE); + } + + public static VariableRepetition ofAtMost(final Element element, final int maxOccurrences) { + return ofBetween(element, 0, maxOccurrences); + } + + public static VariableRepetition ofExactly(final Element element, final int exactly) { + return ofBetween(element, exactly, exactly); + } + + public static VariableRepetition ofBetween(final Element element, final int min, final int max) { + return new VariableRepetition(element, min, max); + } + + private final int minOccurrences; + private final int maxOccurrences; + private final Element element; + + private VariableRepetition(final Element element, final int minOccurrences, final int maxOccurrences) { + this.minOccurrences = minOccurrences; + this.maxOccurrences = maxOccurrences; + this.element = Objects.requireNonNull(element); + } + + @Override + public boolean isValidFor(final int codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 17 * hash + this.minOccurrences; + hash = 17 * hash + this.maxOccurrences; + hash = 17 * hash + Objects.hashCode(this.element); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final VariableRepetition other = (VariableRepetition) obj; + if (this.minOccurrences != other.minOccurrences) { + return false; + } + if (this.maxOccurrences != other.maxOccurrences) { + return false; + } + return Objects.equals(this.element, other.element); + } + + @Override + public String toString() { + return ( + "VariableRepetition{" + + "minOccurrences=" + + minOccurrences + + ", maxOccurrences=" + + maxOccurrences + + ", element=" + + element + + '}' + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java new file mode 100644 index 00000000..6b798214 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java @@ -0,0 +1,314 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class CoreRulesTest { + + @Nested + class Alpha { + + @Test + void should_return_rulename() { + assertThat(CoreRules.ALPHA.asRuleName(), is(RuleName.of("ALPHA"))); + } + + @ParameterizedTest(name = "{0} is a valid alpha") + @ValueSource(ints = { 0x41, 0x51, 0x5A, 0x61, 0x71, 0x7A }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.ALPHA.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid alpha") + @ValueSource(ints = { 0x30, 0x39, 0x5B, 0x60, 0x7B }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.ALPHA.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Bit { + + @Test + void should_be_valid_for_codepoint_of_zero() { + assertThat(CoreRules.BIT.isValidFor(0x30), is(true)); + } + + @Test + void should_be_valid_for_codepoint_of_one() { + assertThat(CoreRules.BIT.isValidFor(0x31), is(true)); + } + + @Test + void should_be_invalid_for_codepoint_of_two() { + assertThat(CoreRules.BIT.isValidFor(0x32), is(false)); + } + } + + @Nested + class Char { + + @ParameterizedTest(name = "{0} is a valid char") + @ValueSource(ints = { 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x7F }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.CHAR.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid char") + @ValueSource(ints = { 0x00, 0x80, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0xFF }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.CHAR.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Cr { + + @Test + void should_be_valid_for_cr() { + assertThat(CoreRules.CR.isValidFor(0x0D), is(true)); + } + + @Test + void should_be_invalid_for_whitespace() { + assertThat(CoreRules.CR.isValidFor(0x00), is(false)); + } + + @Test + void should_be_invalid_for_linefeed() { + assertThat(CoreRules.CR.isValidFor(0x0A), is(false)); + } + + @Test + void should_be_invalid_for_a() { + assertThat(CoreRules.CR.isValidFor(0x41), is(false)); + } + } + + @Nested + class Digit { + + @Test + void should_return_rulename() { + assertThat(CoreRules.DIGIT.asRuleName(), is(RuleName.of("DIGIT"))); + } + + @ParameterizedTest(name = "{0} is a valid digit") + @ValueSource(ints = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.DIGIT.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid dig") + @ValueSource(ints = { 0x00, 0x10, 0x21, 0x29, 0x40, 0x50, 0x60 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.DIGIT.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Dquote { + + @Test + void should_be_valid() { + assertThat(CoreRules.DQUOTE.isValidFor(0x22), is(true)); + } + + @Test + void should_be_invalid() { + assertThat(CoreRules.DQUOTE.isValidFor(0x21), is(false)); + } + } + + @Nested + class Hexdig { + + @ParameterizedTest(name = "{0} is a valid hexdig") + @ValueSource( + ints = { + 0x30, + 0x31, + 0x32, + 0x33, + 0x34, + 0x35, + 0x36, + 0x37, + 0x38, + 0x39, + 0x41, + 0x42, + 0x43, + 0x44, + 0x45, + 0x46, + 0x61, + 0x62, + 0x64, + 0x65, + 0x66, + } + ) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.HEXDIG.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid hexdig") + @ValueSource(ints = { 0x00, 0x10, 0x21, 0x29, 0x47, 0x50, 0x60, 0x67, 0x71 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.HEXDIG.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Htab { + + @Test + void should_be_valid_for_htab() { + assertThat(CoreRules.HTAB.isValidFor(0x09), is(true)); + } + + @Test + void should_be_invalid_for_whitespace() { + assertThat(CoreRules.HTAB.isValidFor(0x00), is(false)); + } + + @Test + void should_be_invalid_for_linefeed() { + assertThat(CoreRules.HTAB.isValidFor(0x0A), is(false)); + } + + @Test + void should_be_invalid_for_a() { + assertThat(CoreRules.HTAB.isValidFor(0x41), is(false)); + } + } + + @Nested + class Lf { + + @Test + void should_return_rulename() { + assertThat(CoreRules.LF.asRuleName(), is(RuleName.of("LF"))); + } + + @Test + void should_be_valid_for_linefeed() { + assertThat(CoreRules.LF.isValidFor(0x0A), is(true)); + } + + @Test + void should_be_invalid_for_whitespace() { + assertThat(CoreRules.LF.isValidFor(0x00), is(false)); + } + + @Test + void should_be_invalid_for_cr() { + assertThat(CoreRules.LF.isValidFor(0x0D), is(false)); + } + + @Test + void should_be_invalid_for_a() { + assertThat(CoreRules.LF.isValidFor(0x41), is(false)); + } + } + + @Nested + class Octet { + + @ParameterizedTest(name = "{0} is a valid octet") + @ValueSource(ints = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xA1, 0xFF }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.OCTET.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid octet") + @ValueSource(ints = { 0x100, 0x101 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.OCTET.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Space { + + @Test + void should_be_valid_for_space() { + assertThat(CoreRules.SP.isValidFor(0x20), is(true)); + } + + @Test + void should_be_invalid_for_tab() { + assertThat(CoreRules.SP.isValidFor(0x09), is(false)); + } + + @Test + void should_be_invalid_for_linefeed() { + assertThat(CoreRules.SP.isValidFor(0x0A), is(false)); + } + } + + @Nested + class VisibleCharacters { + + @ParameterizedTest(name = "{0} is a valid vchat") + @ValueSource(ints = { 0x21, 0x31, 0x42, 0x55, 0x65, 0x71, 0x7E }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.VCHAR.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid dig") + @ValueSource(ints = { 0x00, 0x10, 0x20, 0x7F, 0x80 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.VCHAR.isValidFor(codePoint), is(false)); + } + } + + @Nested + class WhiteSpace { + + @Test + void should_be_valid_for_space() { + assertThat(CoreRules.WSP.isValidFor(0x20), is(true)); + } + + @Test + void should_be_valid_for_htab() { + assertThat(CoreRules.WSP.isValidFor(0x09), is(true)); + } + + @Test + void should_be_valid_for_linefeed() { + assertThat(CoreRules.WSP.isValidFor(0x0A), is(false)); + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java new file mode 100644 index 00000000..a9367d8d --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class RuleListTest { + + @Test + void should_create_new() { + assertThat(RuleList.of(Rule.of(RuleName.of("test"), StringElement.of("test"))), is(not(nullValue()))); + } + + @Test + void equalsContract() { + EqualsVerifier.forClass(RuleList.class).verify(); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java new file mode 100644 index 00000000..8a1d89e7 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class RuleTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(Rule.class).verify(); + } + + @Test + void should_know_his_name() { + final Rule rule = Rule.of(RuleName.of("date"), RuleReference.of(RuleName.of("DIGIT"))); + + assertThat(rule.hasRuleName(RuleName.of("date")), is(true)); + assertThat(rule.hasRuleName(RuleName.of("iso-date-time")), is(false)); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java new file mode 100644 index 00000000..060ee140 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import java.util.List; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class AlternativeTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(Alternative.class).verify(); + } + + @Test + void should_be_created_an_equals_instance() { + assertThat( + Alternative.of(List.of(StringElement.of("a"), StringElement.of("b"))), + both(is(Alternative.of(StringElement.of("a"), StringElement.of("b")))).and(not(nullValue())) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java new file mode 100644 index 00000000..03a1aaf7 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import java.util.List; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class ConcatenationTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(Concatenation.class).verify(); + } + + @Test + void should_be_created_an_equals_instance() { + assertThat( + Concatenation.of(List.of(StringElement.of("a"), StringElement.of("b"))), + both(is(Concatenation.of(StringElement.of("a"), StringElement.of("b")))).and(not(nullValue())) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java new file mode 100644 index 00000000..71743f81 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java @@ -0,0 +1,69 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class NumericCharacterTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(NumericCharacter.class).withOnlyTheseFields("value").verify(); + } + + @Test + void should_parse_int_with_dec_radix() { + assertThat(NumericCharacter.BASE.DECIMAL.convert("11"), is(11)); + } + + @Test + void should_parse_int_with_bin_radix() { + assertThat(NumericCharacter.BASE.BINARY.convert("11"), is(3)); + } + + @Test + void should_create_bin_base() { + assertThat(NumericCharacter.BASE.findByShortName('b'), is(NumericCharacter.BASE.BINARY)); + } + + @Test + void should_create_dec_base() { + assertThat(NumericCharacter.BASE.findByShortName('d'), is(NumericCharacter.BASE.DECIMAL)); + } + + @Test + void should_create_hex_base() { + assertThat(NumericCharacter.BASE.findByShortName('x'), is(NumericCharacter.BASE.HEXADECIMAL)); + } + + @Test + void should_throw_exception_for_invalid_shortname() { + assertThrows(IllegalArgumentException.class, () -> NumericCharacter.BASE.findByShortName('h')); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java new file mode 100644 index 00000000..5bb72885 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java @@ -0,0 +1,45 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class OptionalSequenceTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(OptionalSequence.class).verify(); + } + + @Test + void should_create_new_optionalsequence() { + assertThat(OptionalSequence.of(StringElement.of(".")), is(not(nullValue()))); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java new file mode 100644 index 00000000..11ae7f2f --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java @@ -0,0 +1,35 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class RuleNameTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(RuleName.class).withNonnullFields("name").verify(); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java new file mode 100644 index 00000000..28a2f3f6 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java @@ -0,0 +1,45 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class RuleReferenceTest { + + @Test + void should_create_new() { + assertThat(RuleReference.of(RuleName.of("name")), is(not(nullValue()))); + } + + @Test + void equalsContract() { + EqualsVerifier.forClass(RuleReference.class).verify(); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java new file mode 100644 index 00000000..d4882d40 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java @@ -0,0 +1,52 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class SequenceGroupTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(SequenceGroup.class).verify(); + } + + @Test + void should_create() { + assertThat( + SequenceGroup.of( + StringElement.of("test"), + RuleReference.of(RuleName.of("test")), + SequenceGroup.of(NumericCharacter.of(NumericCharacter.BASE.BINARY, 0)) + ), + is(not(nullValue())) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java new file mode 100644 index 00000000..ce10260e --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java @@ -0,0 +1,45 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class SpecificRepetitionTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(SpecificRepetition.class).verify(); + } + + @Test + void should_create_new_element() { + assertThat(SpecificRepetition.of(StringElement.of("1"), 1), is(not(nullValue()))); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java new file mode 100644 index 00000000..f3ef17db --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class StringElementTest { + + @Test + void should_create_new() { + assertThat(StringElement.of("test"), is(not(nullValue()))); + } + + @Test + void equalsContract() { + EqualsVerifier.forClass(StringElement.class).withNonnullFields("value").verify(); + } + + @Test + void should_throw_execption_if_string_has_more_than_one_character() { + final StringElement element = StringElement.of("invalid"); + assertThrows(UnsupportedOperationException.class, () -> element.isValidFor(0x01)); + } + + @Test + void should_be_valid_if_codePoint_is_lowercase_representation() { + assertThat(StringElement.of("A").isValidFor(0x61), is(true)); + } + + @Test + void should_be_valid_if_codePoint_is_upercase_representation() { + assertThat(StringElement.of("A").isValidFor(0x41), is(true)); + } + + @Test + void should_be_invalid_if_codePoint_is_neither_upercase_nor_lowercase_representation() { + assertThat(StringElement.of("A").isValidFor(0x42), is(false)); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java new file mode 100644 index 00000000..d665fb77 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java @@ -0,0 +1,116 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class ValueRangeAlternativesTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(ValueRangeAlternatives.class).verify(); + } + + @Test + void should_create_a_new_value_range() { + assertThat( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0), + NumericCharacter.of(NumericCharacter.BASE.BINARY, 1) + ), + is(not(nullValue())) + ); + } + + @Test + void should_be_invalid_if_value_too_small() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(0x01), + is(false) + ); + } + + @Test + void should_be_invalid_if_value_too_big() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(0x05), + is(false) + ); + } + + @Test + void should_be_valid_if_value_is_in_range() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(0x03), + is(true) + ); + } + + @Test + void should_be_valid_if_value_is_minimum() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(0x02), + is(true) + ); + } + + @Test + void should_be_valid_if_value_is_maximum() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(0x04), + is(true) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java new file mode 100644 index 00000000..747d491f --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class VariableRepetitionTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(VariableRepetition.class).verify(); + } + + @Test + void should_create_an_exactly() { + assertThat( + VariableRepetition.ofExactly(RuleReference.of(RuleName.of("test")), 2), + is(VariableRepetition.ofBetween(RuleReference.of(RuleName.of("test")), 2, 2)) + ); + } + + @Test + void should_create_new_at_most_element() { + assertThat(VariableRepetition.ofAtMost(StringElement.of("1"), 2), is(not(nullValue()))); + } + + @Test + void should_create_new_at_least_element() { + assertThat(VariableRepetition.ofAtLeast(StringElement.of("1"), 2), is(not(nullValue()))); + } + + @Test + void should_create_new_optional_element() { + assertThat(VariableRepetition.of(StringElement.of("1")), is(not(nullValue()))); + } +} diff --git a/vocabulary-format-assertion/src/test/java/module-info.java b/vocabulary-format-assertion/src/test/java/module-info.java new file mode 100644 index 00000000..dca81c0a --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/module-info.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Copyright 2023 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. + */ + +open module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { + requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; + requires io.github.sebastiantoepfer.jsonschema; + requires jakarta.json; + + requires org.junit.jupiter.api; + requires org.junit.jupiter.params; + requires org.hamcrest; + requires nl.jqno.equalsverifier; +} From 59e120c4a5d8facc8d24df2e6a1cf3edfd44dd12 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Wed, 15 Nov 2023 10:30:22 +0100 Subject: [PATCH 03/11] add a very basic abnf reader --- .../format/assertion/abnf/reader/ABNF.java | 30 ++ .../abnf/reader/AlternativeExtractor.java | 99 +++++ .../abnf/reader/CharValExtractor.java | 64 +++ .../CodePointBasedElementEndDelimiter.java | 54 +++ .../abnf/reader/ConcatenationExtractor.java | 108 +++++ .../format/assertion/abnf/reader/Creator.java | 28 ++ .../abnf/reader/ElementEndDetector.java | 33 ++ .../abnf/reader/ElementExtractor.java | 63 +++ .../assertion/abnf/reader/Extractor.java | 32 ++ .../assertion/abnf/reader/ExtractorOwner.java | 28 ++ .../assertion/abnf/reader/GroupExtractor.java | 75 ++++ .../abnf/reader/NewLineDetector.java | 69 ++++ .../abnf/reader/NumValExtractor.java | 172 ++++++++ .../abnf/reader/OptionExtractor.java | 75 ++++ .../abnf/reader/RepetitionExtractor.java | 242 +++++++++++ .../assertion/abnf/reader/RuleExtractor.java | 94 +++++ .../abnf/reader/RuleListExtractor.java | 74 ++++ .../abnf/reader/RuleNameExtractor.java | 73 ++++ .../abnf/reader/RuleReferenceExtractor.java | 64 +++ .../assertion/abnf/reader/TextABNF.java | 55 +++ .../assertion/abnf/reader/TokenCreator.java | 28 ++ .../abnf/reader/UsefulCodepoints.java | 41 ++ .../src/main/java/module-info.java | 1 + .../reader/ConcatenationExtractorTest.java | 124 ++++++ .../abnf/reader/ElementExtractorTest.java | 88 ++++ .../assertion/abnf/reader/FakeOwner.java | 57 +++ .../abnf/reader/GroupExtractorTest.java | 80 ++++ .../abnf/reader/NewLineDetectorTest.java | 107 +++++ .../abnf/reader/NumValExtractorTest.java | 104 +++++ .../abnf/reader/OptionExtractorTest.java | 64 +++ .../abnf/reader/RepetitionExtractorTest.java | 122 ++++++ .../abnf/reader/RuleExtractorTest.java | 73 ++++ .../abnf/reader/RuleListExtractorTest.java | 78 ++++ .../abnf/reader/RuleNameExtractorTest.java | 80 ++++ .../assertion/abnf/reader/TextABNFTest.java | 388 ++++++++++++++++++ .../src/test/java/module-info.java | 1 + 36 files changed, 2968 insertions(+) create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java new file mode 100644 index 00000000..3b2d3f30 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java @@ -0,0 +1,30 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; + +public interface ABNF { + RuleList rules(); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java new file mode 100644 index 00000000..5f83b621 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java @@ -0,0 +1,99 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.SOLIDUS; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +class AlternativeExtractor implements Extractor, ExtractorOwner { + + static Extractor of( + final ExtractorOwner owner, + final Element element, + final ElementEndDetector endOfElementDelimiter + ) { + return new AlternativeExtractor(owner, List.of(element), endOfElementDelimiter, RepetitionExtractor::of); + } + + private final ExtractorOwner owner; + private final List elements; + private final ElementEndDetector endOfElementDetector; + private final Function newElement; + + private AlternativeExtractor( + final ExtractorOwner owner, + final Collection elements, + final ElementEndDetector endOfElementDelimiter, + final Function newElement + ) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + this.endOfElementDetector = Objects.requireNonNull(endOfElementDelimiter); + this.newElement = Objects.requireNonNull(newElement); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + final ElementEndDetector currentEndOfElementDetector = endOfElementDetector.append(codePoint); + if (currentEndOfElementDetector.isEndReached()) { + result = currentEndOfElementDetector.applyTo(owner.imDone(asCreator())); + } else if (Character.isWhitespace(codePoint)) { + result = new AlternativeExtractor(owner, elements, currentEndOfElementDetector, newElement); + } else if (codePoint == SOLIDUS) { + result = new AlternativeExtractor(owner, elements, currentEndOfElementDetector, RepetitionExtractor::of); + } else { + result = newElement.apply(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final Collection newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new AlternativeExtractor(owner, newElements, endOfElementDetector, p -> owner.imDone(p.asCreator())); + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(Alternative.of(elements)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java new file mode 100644 index 00000000..5f22737e --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.QUOTATION_MARK; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import java.util.Objects; + +class CharValExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new CharValExtractor(owner, ""); + } + + private final ExtractorOwner owner; + private final String value; + + private CharValExtractor(final ExtractorOwner owner, final String value) { + this.owner = Objects.requireNonNull(owner); + this.value = Objects.requireNonNull(value); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == QUOTATION_MARK) { + result = owner.imDone(asCreator()); + } else { + result = new CharValExtractor(owner, value.concat(Character.toString(codePoint))); + } + return result; + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(StringElement.of(value)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java new file mode 100644 index 00000000..e94e7bfe --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java @@ -0,0 +1,54 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +class CodePointBasedElementEndDelimiter implements ElementEndDetector { + + private final int stop; + private final boolean reachStop; + + CodePointBasedElementEndDelimiter(final int stop) { + this(stop, false); + } + + private CodePointBasedElementEndDelimiter(final int stop, final boolean reachStop) { + this.stop = stop; + this.reachStop = reachStop; + } + + @Override + public ElementEndDetector append(final int codePoint) { + return new CodePointBasedElementEndDelimiter(stop, codePoint == stop); + } + + @Override + public boolean isEndReached() { + return reachStop; + } + + @Override + public Extractor applyTo(final Extractor imDone) { + return imDone.append(stop); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java new file mode 100644 index 00000000..0b43c482 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java @@ -0,0 +1,108 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.SOLIDUS; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class ConcatenationExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new ConcatenationExtractor(owner, List.of(), new NewLineDetector()); + } + + static Extractor of(final ExtractorOwner owner, final int stop) { + return new ConcatenationExtractor(owner, List.of(), new CodePointBasedElementEndDelimiter(stop)); + } + + private final ExtractorOwner owner; + private final List elements; + private final ElementEndDetector endOfElementDelimiter; + + private ConcatenationExtractor( + final ExtractorOwner owner, + final Collection elements, + final ElementEndDetector endOfElementDelimiter + ) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + this.endOfElementDelimiter = endOfElementDelimiter; + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + final ElementEndDetector currentEndOfElementDetector = endOfElementDelimiter.append(codePoint); + if (currentEndOfElementDetector.isEndReached()) { + result = currentEndOfElementDetector.applyTo(owner.imDone(asCreator())); + } else if (Character.isWhitespace(codePoint)) { + result = new ConcatenationExtractor(owner, elements, currentEndOfElementDetector); + } else if (codePoint == SOLIDUS) { + final List newElements = new ArrayList<>(elements); + final Element element = newElements.remove(newElements.size() - 1); + result = + AlternativeExtractor.of( + new ConcatenationExtractor(owner, newElements, currentEndOfElementDetector), + element, + currentEndOfElementDetector + ); + } else { + result = RepetitionExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + final Element result; + if (elements.size() == 1) { + result = elements.iterator().next(); + } else { + result = Concatenation.of(elements); + } + return cls.cast(result); + } + }; + } + + @Override + public Extractor imDone(final Creator creator) { + final Collection newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new ConcatenationExtractor(owner, newElements, endOfElementDelimiter); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java new file mode 100644 index 00000000..5ba550ed --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +interface Creator { + T createAs(Class cls); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java new file mode 100644 index 00000000..226c3970 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +interface ElementEndDetector extends Extractor { + @Override + ElementEndDetector append(int codePoint); + + boolean isEndReached(); + + Extractor applyTo(Extractor imDone); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java new file mode 100644 index 00000000..666ba2c2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.LEFT_PARENTHESIS; +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.LEFT_SQUARE_BRACKET; +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.PERCENT_SIGN; +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.QUOTATION_MARK; + +import java.util.Objects; + +final class ElementExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new ElementExtractor(owner); + } + + private final ExtractorOwner owner; + + private ElementExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleReferenceExtractor.of(owner).append(codePoint); + } else if (codePoint == LEFT_PARENTHESIS) { + result = GroupExtractor.of(owner); + } else if (codePoint == LEFT_SQUARE_BRACKET) { + result = OptionExtractor.of(owner); + } else if (codePoint == QUOTATION_MARK) { + result = CharValExtractor.of(owner); + } else if (codePoint == PERCENT_SIGN) { + result = NumValExtractor.of(owner); + } else { + result = this; + } + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java new file mode 100644 index 00000000..4bd2d757 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java @@ -0,0 +1,32 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +interface Extractor { + Extractor append(int codePoint); + + default Creator finish() { + throw new IllegalStateException("current extractor can not be finished!"); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java new file mode 100644 index 00000000..b27bb5bb --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +interface ExtractorOwner { + Extractor imDone(Creator creator); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java new file mode 100644 index 00000000..e2cbc9f7 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.RIGHT_PARENTHESIS; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +final class GroupExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new GroupExtractor(owner, List.of()); + } + + private final ExtractorOwner owner; + private final List elements; + + private GroupExtractor(final ExtractorOwner owner, final Collection elements) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == RIGHT_PARENTHESIS) { + result = owner.imDone(asCreator()); + } else { + result = ConcatenationExtractor.of(this, RIGHT_PARENTHESIS).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final List newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new GroupExtractor(owner, newElements); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(SequenceGroup.of(elements)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java new file mode 100644 index 00000000..93b3dbd2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java @@ -0,0 +1,69 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +class NewLineDetector implements ElementEndDetector { + + private final boolean cr; + private final boolean lf; + private final int lastCodePoint; + + public NewLineDetector() { + this(false, false, 0); + } + + private NewLineDetector(final boolean cr, final boolean lf, final int lastCodePoint) { + this.cr = cr; + this.lf = lf; + this.lastCodePoint = lastCodePoint; + } + + @Override + public NewLineDetector append(final int codePoint) { + final NewLineDetector result; + if (lf) { + result = + new NewLineDetector( + cr && !Character.isWhitespace(codePoint), + !Character.isWhitespace(codePoint), + codePoint + ); + } else if (cr) { + result = new NewLineDetector(codePoint == '\n', codePoint == '\n', codePoint); + } else { + result = new NewLineDetector(codePoint == '\r', false, codePoint); + } + return result; + } + + @Override + public boolean isEndReached() { + return cr && lf && !Character.isWhitespace(lastCodePoint); + } + + @Override + public Extractor applyTo(final Extractor imDone) { + return imDone.append('\r').append('\n').append(lastCodePoint); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java new file mode 100644 index 00000000..4d99589c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java @@ -0,0 +1,172 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import java.util.Objects; + +class NumValExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new NumValExtractor(owner); + } + + private final ExtractorOwner owner; + + private NumValExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final NumericCharacter.BASE base = NumericCharacter.BASE.findByShortName(Character.toChars(codePoint)[0]); + final CoreRules rule = + switch (base) { + case BINARY -> CoreRules.BIT; + case DECIMAL -> CoreRules.DIGIT; + case HEXADECIMAL -> CoreRules.HEXDIG; + default -> throw new AssertionError(); + }; + return new SpecificNumValExtractor(owner, base, rule); + } + + private static class SpecificNumValExtractor implements Extractor { + + private final ExtractorOwner owner; + private final NumericCharacter.BASE base; + private final CoreRules rule; + private final String value; + + public SpecificNumValExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule + ) { + this(owner, base, rule, "0"); + } + + public SpecificNumValExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule, + final String value + ) { + this.owner = Objects.requireNonNull(owner); + this.base = Objects.requireNonNull(base); + this.rule = Objects.requireNonNull(rule); + this.value = Objects.requireNonNull(value); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == UsefulCodepoints.HYPHEN_MINUS) { + result = new ValueRangeExtractor(owner, base, rule, value); + } else if (Character.isWhitespace(codePoint) || !rule.isValidFor(codePoint)) { + result = owner.imDone(asCreator()).append(codePoint); + } else { + result = new SpecificNumValExtractor(owner, base, rule, value.concat(Character.toString(codePoint))); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(NumericCharacter.of(base, base.convert(value))); + } + }; + } + } + + private static class ValueRangeExtractor implements Extractor { + + private final ExtractorOwner owner; + private final NumericCharacter.BASE base; + private final CoreRules rule; + private final String start; + private final String end; + + public ValueRangeExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule, + final String start + ) { + this(owner, base, rule, start, "0"); + } + + private ValueRangeExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule, + final String start, + final String end + ) { + this.owner = Objects.requireNonNull(owner); + this.base = Objects.requireNonNull(base); + this.rule = Objects.requireNonNull(rule); + this.start = Objects.requireNonNull(start); + this.end = Objects.requireNonNull(end); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isWhitespace(codePoint) || !rule.isValidFor(codePoint)) { + result = owner.imDone(asCreator()).append(codePoint); + } else { + result = new ValueRangeExtractor(owner, base, rule, start, end.concat(Character.toString(codePoint))); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + ValueRangeAlternatives.of( + NumericCharacter.of(base, base.convert(start)), + NumericCharacter.of(base, base.convert(end)) + ) + ); + } + }; + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java new file mode 100644 index 00000000..08738749 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.RIGHT_SQUARE_BRACKET; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class OptionExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new OptionExtractor(owner, List.of()); + } + + private final ExtractorOwner owner; + private final List elements; + + private OptionExtractor(final ExtractorOwner owner, final Collection elements) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == RIGHT_SQUARE_BRACKET) { + result = owner.imDone(asCreator()); + } else { + result = ConcatenationExtractor.of(this, RIGHT_SQUARE_BRACKET).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final Collection newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new OptionExtractor(owner, newElements); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(OptionalSequence.of(elements)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java new file mode 100644 index 00000000..6b8853af --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java @@ -0,0 +1,242 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.ASTERISK; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SpecificRepetition; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import java.util.Objects; + +class RepetitionExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new RepetitionExtractor(owner); + } + + private final ExtractorOwner owner; + + private RepetitionExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + Extractor result; + if (Character.isDigit(codePoint)) { + result = new SpecificRepetitionExtractor(owner, Character.toString(codePoint)); + } else if (codePoint == ASTERISK) { + result = new UnspecificRepetitionExtractor(owner); + } else { + result = ElementExtractor.of(owner).append(codePoint); + } + return result; + } + + private static class SpecificRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String repeat; + + public SpecificRepetitionExtractor(final ExtractorOwner owner, final String repeat) { + this.owner = Objects.requireNonNull(owner); + this.repeat = Objects.requireNonNull(repeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == ASTERISK) { + result = new AtLeastRepetitionExtractor(owner, repeat); + } else if (Character.isDigit(codePoint)) { + result = new SpecificRepetitionExtractor(owner, repeat.concat(Character.toString(codePoint))); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + SpecificRepetition.of(creator.createAs(Element.class), Integer.parseInt(repeat)) + ); + } + } + ); + } + } + + private static class AtLeastRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String minRepeat; + + private AtLeastRepetitionExtractor(final ExtractorOwner owner, final String minRepeat) { + this.owner = Objects.requireNonNull(owner); + this.minRepeat = Objects.requireNonNull(minRepeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = new BetweenRepetitionExtractor(owner, minRepeat, Character.toString(codePoint)); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + VariableRepetition.ofAtLeast(creator.createAs(Element.class), Integer.parseInt(minRepeat)) + ); + } + } + ); + } + } + + private static class UnspecificRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + + public UnspecificRepetitionExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = new AtMostRepetitionExtractor(owner, Character.toString(codePoint)); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(VariableRepetition.of(creator.createAs(Element.class))); + } + } + ); + } + } + + private static class AtMostRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String maxRepeat; + + public AtMostRepetitionExtractor(final ExtractorOwner owner, final String maxRepeat) { + this.owner = Objects.requireNonNull(owner); + this.maxRepeat = Objects.requireNonNull(maxRepeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = new AtMostRepetitionExtractor(owner, maxRepeat.concat(Character.toString(codePoint))); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + VariableRepetition.ofAtMost(creator.createAs(Element.class), Integer.parseInt(maxRepeat)) + ); + } + } + ); + } + } + + private static class BetweenRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String minRepeat; + private final String maxRepeat; + + public BetweenRepetitionExtractor(final ExtractorOwner owner, final String minRepeat, final String maxRepeat) { + this.owner = Objects.requireNonNull(owner); + this.minRepeat = Objects.requireNonNull(minRepeat); + this.maxRepeat = Objects.requireNonNull(maxRepeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = + new BetweenRepetitionExtractor(owner, minRepeat, maxRepeat.concat(Character.toString(codePoint))); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + VariableRepetition.ofBetween( + creator.createAs(Element.class), + Integer.parseInt(minRepeat), + Integer.parseInt(maxRepeat) + ) + ); + } + } + ); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java new file mode 100644 index 00000000..5cd2d66c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.EQUALS_SIGN; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import java.util.Objects; + +final class RuleExtractor implements Extractor, ExtractorOwner { + + public static Extractor of(final ExtractorOwner owner) { + return new RuleExtractor(owner); + } + + private final ExtractorOwner owner; + + private RuleExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleNameExtractor.of(this).append(codePoint); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return new DefinedAsExtractor(owner, creator.createAs(RuleName.class)); + } + + private static class DefinedAsExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final RuleName ruleName; + + public DefinedAsExtractor(final ExtractorOwner owner, final RuleName ruleName) { + this.owner = Objects.requireNonNull(owner); + this.ruleName = Objects.requireNonNull(ruleName); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == EQUALS_SIGN) { + result = ConcatenationExtractor.of(this); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(Rule.of(ruleName, creator.createAs(Element.class))); + } + } + ); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java new file mode 100644 index 00000000..0c3c30f5 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java @@ -0,0 +1,74 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import java.util.ArrayList; +import java.util.List; + +final class RuleListExtractor implements Extractor, ExtractorOwner { + + public static Extractor of() { + return new RuleListExtractor(); + } + + private final List rules; + + private RuleListExtractor() { + this(List.of()); + } + + private RuleListExtractor(final List rules) { + this.rules = List.copyOf(rules); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleExtractor.of(this).append(codePoint); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final List newRules = new ArrayList<>(rules); + newRules.add(creator.createAs(Rule.class)); + return new RuleListExtractor(newRules); + } + + @Override + public Creator finish() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(RuleList.of(rules)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java new file mode 100644 index 00000000..6869d1b7 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java @@ -0,0 +1,73 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import java.util.Objects; + +final class RuleNameExtractor implements Extractor { + + public static Extractor of(final ExtractorOwner owner) { + return new RuleNameExtractor(owner); + } + + private final ExtractorOwner owner; + private final String name; + + private RuleNameExtractor(final ExtractorOwner owner) { + this(owner, ""); + } + + private RuleNameExtractor(final ExtractorOwner owner, final String name) { + this.owner = Objects.requireNonNull(owner); + this.name = Objects.requireNonNull(name); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (name.isEmpty() && !Character.isLetter(codePoint)) { + throw new IllegalArgumentException("rulename must start with a letter!"); + } else if (Character.isLetterOrDigit(codePoint) || codePoint == UsefulCodepoints.HYPHEN_MINUS) { + result = new RuleNameExtractor(owner, name.concat(Character.toString(codePoint))); + } else { + result = owner.imDone(asCreator()).append(codePoint); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(RuleName.of(name)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java new file mode 100644 index 00000000..e8088920 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import java.util.Objects; + +class RuleReferenceExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new RuleReferenceExtractor(owner); + } + + private final ExtractorOwner owner; + + private RuleReferenceExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleNameExtractor.of(this).append(codePoint); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(RuleReference.of(creator.createAs(RuleName.class))); + } + } + ); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java new file mode 100644 index 00000000..0461162c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java @@ -0,0 +1,55 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import java.util.logging.Logger; + +public final class TextABNF implements ABNF { + + private static final Logger LOG = Logger.getLogger(TextABNF.class.getName()); + + public static TextABNF of(final String rules) { + return new TextABNF(rules); + } + + private final String rules; + + private TextABNF(final String rules) { + this.rules = rules; + } + + @Override + public RuleList rules() { + LOG.entering(TextABNF.class.getName(), "rules"); + final RuleList result = rules + .codePoints() + .boxed() + .reduce(RuleListExtractor.of(), Extractor::append, (l, r) -> null) + .finish() + .createAs(RuleList.class); + LOG.exiting(TextABNF.class.getName(), "rules", result); + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java new file mode 100644 index 00000000..034bfc5c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +interface TokenCreator { + T create(); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java new file mode 100644 index 00000000..f56f7d9d --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java @@ -0,0 +1,41 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +final class UsefulCodepoints { + + static final int QUOTATION_MARK = 0x22; + static final int PERCENT_SIGN = 0x25; + static final int LEFT_PARENTHESIS = 0x28; + static final int RIGHT_PARENTHESIS = 0x29; + static final int ASTERISK = 0x2A; + static final int HYPHEN_MINUS = 0x2D; + static final int SOLIDUS = 0x2F; + static final int SEMICOLON = 0x3B; + static final int EQUALS_SIGN = 0x3D; + static final int LEFT_SQUARE_BRACKET = 0x5B; + static final int RIGHT_SQUARE_BRACKET = 0x5D; + + private UsefulCodepoints() {} +} diff --git a/vocabulary-format-assertion/src/main/java/module-info.java b/vocabulary-format-assertion/src/main/java/module-info.java index 66c685cd..a0041318 100644 --- a/vocabulary-format-assertion/src/main/java/module-info.java +++ b/vocabulary-format-assertion/src/main/java/module-info.java @@ -24,6 +24,7 @@ module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; requires io.github.sebastiantoepfer.jsonschema; + requires java.logging; requires jakarta.json; provides io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java new file mode 100644 index 00000000..61d2cd2e --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java @@ -0,0 +1,124 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class ConcatenationExtractorTest { + + @Test + void should_create_rulereference() { + assertThat( + ConcatenationExtractor.of(new FakeOwner()).append('a').append(' ').finish().createAs(Element.class), + is(RuleReference.of(RuleName.of("a"))) + ); + } + + @Test + void should_create_repeating_rulereference() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('1') + .append('*') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("a")), 1)) + ); + } + + @Test + void should_create_elements_from_multiline() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('a') + .append('\r') + .append('\n') + .append(' ') + .append('b') + .append(' ') + .finish() + .createAs(Element.class), + is(Concatenation.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b")))) + ); + } + + @Test + void should_create_elements_as_alternative() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('a') + .append('/') + .append('b') + .append(' ') + .finish() + .createAs(Element.class), + is(Alternative.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b")))) + ); + } + + @Test + void should_create_elements_as_mixed_with_alternative() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('b') + .append('/') + .append('c') + .append('/') + .append('d') + .append(' ') + .append('e') + .append(' ') + .finish() + .createAs(Element.class), + is( + Concatenation.of( + RuleReference.of(RuleName.of("a")), + Alternative.of( + RuleReference.of(RuleName.of("b")), + RuleReference.of(RuleName.of("c")), + RuleReference.of(RuleName.of("d")) + ), + RuleReference.of(RuleName.of("e")) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java new file mode 100644 index 00000000..2c58d45a --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java @@ -0,0 +1,88 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import org.junit.jupiter.api.Test; + +class ElementExtractorTest { + + @Test + void should_create_rulereference() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('a').finish().createAs(Element.class), + is(RuleReference.of(RuleName.of("a"))) + ); + } + + @Test + void should_create_group() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('(').append('a').append(')').finish().createAs(Element.class), + is(SequenceGroup.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_option() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('[').append('a').append(']').finish().createAs(Element.class), + is(OptionalSequence.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_char_val() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('"').append('a').append('"').finish().createAs(Element.class), + is(StringElement.of("a")) + ); + } + + @Test + void should_num_val() { + assertThat( + ElementExtractor + .of(new FakeOwner()) + .append('%') + .append('b') + .append('1') + .append('0') + .append('1') + .append('0') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.BINARY, 10)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java new file mode 100644 index 00000000..b052a2de --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java @@ -0,0 +1,57 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +final class FakeOwner implements Extractor, ExtractorOwner, Creator { + + private final Creator creator; + + FakeOwner() { + this(null); + } + + private FakeOwner(final Creator creator) { + this.creator = creator; + } + + @Override + public Extractor append(final int codePoint) { + return this; + } + + @Override + public Extractor imDone(final Creator creator) { + return new FakeOwner(creator); + } + + @Override + public Creator finish() { + return this; + } + + @Override + public T createAs(final Class cls) { + return creator.createAs(cls); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java new file mode 100644 index 00000000..853d758a --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import org.junit.jupiter.api.Test; + +class GroupExtractorTest { + + @Test + void should_create_group_with_one_element() { + assertThat( + GroupExtractor.of(new FakeOwner()).append('a').append(')').finish().createAs(Element.class), + is(SequenceGroup.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_group_with_more_than_one_element() { + assertThat( + GroupExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('b') + .append(')') + .finish() + .createAs(Element.class), + is( + SequenceGroup.of( + Concatenation.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b"))) + ) + ) + ); + } + + @Test + void should_create_alternative_inside() { + assertThat( + GroupExtractor + .of(new FakeOwner()) + .append('a') + .append('/') + .append('b') + .append(')') + .finish() + .createAs(Element.class), + is(SequenceGroup.of(Alternative.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b"))))) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java new file mode 100644 index 00000000..281426b6 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java @@ -0,0 +1,107 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +class NewLineDetectorTest { + + @Test + void should_not_reach_end_after_cr() { + assertThat(new NewLineDetector().append('\r').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlf() { + assertThat(new NewLineDetector().append('\r').append('\n').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlfwsp() { + assertThat(new NewLineDetector().append('\r').append('\n').append(' ').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlflf() { + assertThat(new NewLineDetector().append('\r').append('\n').append('\n').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crnonwsp() { + assertThat(new NewLineDetector().append('\r').append('a').append('a').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_lfnonwsp() { + assertThat(new NewLineDetector().append('\n').append('a').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlfwspnonwsp() { + assertThat(new NewLineDetector().append('\r').append('\n').append(' ').append('a').isEndReached(), is(false)); + } + + @Test + void should_reach_end_after_crlfnonwsp() { + assertThat(new NewLineDetector().append('\r').append('\n').append('a').isEndReached(), is(true)); + } + + @Test + void should_apply_codepoints() { + assertThat( + new NewLineDetector() + .append('\r') + .append('\n') + .append('a') + .applyTo(new StringExtractor()) + .finish() + .createAs(String.class), + is("\r\na") + ); + } + + private static class StringExtractor implements Extractor { + + private final StringBuilder sb = new StringBuilder(); + + @Override + public Extractor append(final int codePoint) { + sb.appendCodePoint(codePoint); + return this; + } + + @Override + public Creator finish() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(sb.toString()); + } + }; + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java new file mode 100644 index 00000000..00dc4634 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java @@ -0,0 +1,104 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import org.junit.jupiter.api.Test; + +class NumValExtractorTest { + + @Test + void should_create_bin_val() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('b') + .append('0') + .append('1') + .append('0') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.BINARY, 2)) + ); + } + + @Test + void should_create_dec_val() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('d') + .append('3') + .append('1') + .append('0') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 310)) + ); + } + + @Test + void should_create_hex_val() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('x') + .append('3') + .append('a') + .append('A') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x3AA)) + ); + } + + @Test + void should_create_value_range() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('x') + .append('3') + .append('a') + .append('A') + .append('-') + .append('4') + .append('0') + .append('F') + .finish() + .createAs(Element.class), + is( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x3AA), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x40F) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java new file mode 100644 index 00000000..40ed05fe --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import org.junit.jupiter.api.Test; + +class OptionExtractorTest { + + @Test + void should_create_option() { + assertThat( + OptionExtractor.of(new FakeOwner()).append('a').append(']').finish().createAs(Element.class), + is(OptionalSequence.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_option_with_multivalues() { + assertThat( + OptionExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('b') + .append(']') + .finish() + .createAs(Element.class), + is( + OptionalSequence.of( + Concatenation.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b"))) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java new file mode 100644 index 00000000..603c49e5 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SpecificRepetition; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class RepetitionExtractorTest { + + @Test + void should_create_non_repeating_rulereference() { + assertThat( + RepetitionExtractor.of(new FakeOwner()).append('a').append(' ').finish().createAs(Element.class), + is(RuleReference.of(RuleName.of("a"))) + ); + } + + @Test + void should_create_atLeast_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('1') + .append('*') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("a")), 1)) + ); + } + + @Test + void should_create_specificrepetition_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('1') + .append('1') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(SpecificRepetition.of(RuleReference.of(RuleName.of("a")), 11)) + ); + } + + @Test + void should_create_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('*') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_atMost_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('*') + .append('1') + .append('5') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofAtMost(RuleReference.of(RuleName.of("a")), 15)) + ); + } + + @Test + void should_create_between_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('1') + .append('*') + .append('2') + .append('5') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofBetween(RuleReference.of(RuleName.of("a")), 1, 25)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java new file mode 100644 index 00000000..95b4541e --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java @@ -0,0 +1,73 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class RuleExtractorTest { + + @Test + void should_create_rule_with_rulereference() { + assertThat( + RuleExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('=') + .append(' ') + .append('b') + .append('\r') + .append('\n') + .finish() + .createAs(Rule.class), + is(Rule.of(RuleName.of("a"), RuleReference.of(RuleName.of("b")))) + ); + } + + @Test + void should_create_rule_with_repatiting_rulereference() { + assertThat( + RuleExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('=') + .append(' ') + .append('*') + .append('b') + .append('\r') + .append('\n') + .finish() + .createAs(Rule.class), + is(Rule.of(RuleName.of("a"), VariableRepetition.of(RuleReference.of(RuleName.of("b"))))) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java new file mode 100644 index 00000000..51d491e1 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import org.junit.jupiter.api.Test; + +class RuleListExtractorTest { + + @Test + void should_create_list_with_one_rule_with_rulereference() { + assertThat( + RuleListExtractor + .of() + .append('a') + .append('=') + .append('b') + .append('\r') + .append('\n') + .finish() + .createAs(RuleList.class), + is(RuleList.of(Rule.of(RuleName.of("a"), RuleReference.of(RuleName.of("b"))))) + ); + } + + @Test + void should_create_list_with_two_rules_with_rulereference() { + assertThat( + RuleListExtractor + .of() + .append('a') + .append('=') + .append('b') + .append('\r') + .append('\n') + .append('c') + .append('=') + .append('d') + .append('\r') + .append('\n') + .finish() + .createAs(RuleList.class), + is( + RuleList.of( + Rule.of(RuleName.of("a"), RuleReference.of(RuleName.of("b"))), + Rule.of(RuleName.of("c"), RuleReference.of(RuleName.of("d"))) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java new file mode 100644 index 00000000..6b13dd32 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import org.junit.jupiter.api.Test; + +class RuleNameExtractorTest { + + @Test + void should_create_rule_name_from_alpha() { + assertThat( + RuleNameExtractor.of(new FakeOwner()).append('a').append('b').finish().createAs(RuleName.class), + is(RuleName.of("ab")) + ); + } + + @Test + void should_throw_illegal_argument_if_first_codepoint_is_not_a_valid_letter() { + final Extractor extractor = RuleNameExtractor.of(new FakeOwner()); + assertThrows(IllegalArgumentException.class, () -> extractor.append('1')); + } + + @Test + void should_create_rule_name_from_alpha_and_digit() { + assertThat( + RuleNameExtractor.of(new FakeOwner()).append('a').append('1').finish().createAs(RuleName.class), + is(RuleName.of("a1")) + ); + } + + @Test + void should_create_rule_name_from_alpha_and_minus() { + assertThat( + RuleNameExtractor.of(new FakeOwner()).append('a').append('-').append('b').finish().createAs(RuleName.class), + is(RuleName.of("a-b")) + ); + } + + @Test + void should_stop_rule_name_after_first_non_valid_char() { + assertThat( + RuleNameExtractor + .of(new FakeOwner()) + .append('a') + .append('-') + .append('b') + .append(' ') + .append('c') + .finish() + .createAs(RuleName.class), + is(RuleName.of("a-b")) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java new file mode 100644 index 00000000..13ee520c --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java @@ -0,0 +1,388 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class TextABNFTest { + + @Test + void should_create_abnf_rules_from_string() { + //Note: a rule MUST end with crlf!! see rfc5234. lf is already set by the formatting :) + assertThat( + TextABNF + .of( + """ + rulelist = 1*( rule / (*c-wsp c-nl) )\r + rule = rulename defined-as elements c-nl\r + rulename = ALPHA *(ALPHA / DIGIT / "-")\r + defined-as = *c-wsp ("=" / "=/") *c-wsp\r + elements = alternation *c-wsp\r + c-wsp = WSP / (c-nl WSP)\r + c-nl = comment / CRLF\r + comment = ";" *(WSP / VCHAR) CRLF\r + alternation = concatenation\r + *(*c-wsp "/" *c-wsp concatenation)\r + concatenation = repetition *(1*c-wsp repetition)\r + repetition = [repeat] element\r + repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)\r + element = rulename / group / option /\r + char-val / num-val / prose-val\r + group = "(" *c-wsp alternation *c-wsp ")"\r + option = "[" *c-wsp alternation *c-wsp "]"\r + char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE\r + num-val = "%" (bin-val / dec-val / hex-val)\r + bin-val = "b" 1*BIT\r + [ 1*("." 1*BIT) / ("-" 1*BIT) ]\r + dec-val = "d" 1*DIGIT\r + [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]\r + hex-val = "x" 1*HEXDIG\r + [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]\r + """ + ) + .rules(), + is( + RuleList.of( + Rule.of( + RuleName.of("rulelist"), + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Alternative.of( + RuleReference.of(RuleName.of("rule")), + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("c-nl")) + ) + ) + ) + ), + 1 + ) + ), + Rule.of( + RuleName.of("rule"), + Concatenation.of( + RuleReference.of(RuleName.of("rulename")), + RuleReference.of(RuleName.of("defined-as")), + RuleReference.of(RuleName.of("elements")), + RuleReference.of(RuleName.of("c-nl")) + ) + ), + Rule.of( + RuleName.of("rulename"), + Concatenation.of( + RuleReference.of(CoreRules.ALPHA.asRuleName()), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + RuleReference.of(CoreRules.ALPHA.asRuleName()), + RuleReference.of(CoreRules.DIGIT.asRuleName()), + StringElement.of("-") + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("defined-as"), + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + SequenceGroup.of(Alternative.of(StringElement.of("="), StringElement.of("=/"))), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) + ) + ), + Rule.of( + RuleName.of("elements"), + Concatenation.of( + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) + ) + ), + Rule.of( + RuleName.of("c-wsp"), + Alternative.of( + RuleReference.of(RuleName.of("WSP")), + SequenceGroup.of( + Concatenation.of( + RuleReference.of(RuleName.of("c-nl")), + RuleReference.of(CoreRules.WSP.asRuleName()) + ) + ) + ) + ), + Rule.of( + RuleName.of("c-nl"), + Alternative.of( + RuleReference.of(RuleName.of("comment")), + RuleReference.of(CoreRules.CRLF.asRuleName()) + ) + ), + Rule.of( + RuleName.of("comment"), + Concatenation.of( + StringElement.of(";"), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + RuleReference.of(CoreRules.WSP.asRuleName()), + RuleReference.of(CoreRules.VCHAR.asRuleName()) + ) + ) + ), + RuleReference.of(CoreRules.CRLF.asRuleName()) + ) + ), + Rule.of( + RuleName.of("alternation"), + Concatenation.of( + RuleReference.of(RuleName.of("concatenation")), + VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of("/"), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("concatenation")) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("concatenation"), + Concatenation.of( + RuleReference.of(RuleName.of("repetition")), + VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("c-wsp")), 1), + RuleReference.of(RuleName.of("repetition")) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("repetition"), + Concatenation.of( + OptionalSequence.of(RuleReference.of(RuleName.of("repeat"))), + RuleReference.of(RuleName.of("element")) + ) + ), + Rule.of( + RuleName.of("repeat"), + Alternative.of( + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())), + StringElement.of("*"), + VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())) + ) + ) + ) + ), + Rule.of( + RuleName.of("element"), + Alternative.of( + RuleReference.of(RuleName.of("rulename")), + RuleReference.of(RuleName.of("group")), + RuleReference.of(RuleName.of("option")), + RuleReference.of(RuleName.of("char-val")), + RuleReference.of(RuleName.of("num-val")), + RuleReference.of(RuleName.of("prose-val")) + ) + ), + Rule.of( + RuleName.of("group"), + Concatenation.of( + StringElement.of("("), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of(")") + ) + ), + Rule.of( + RuleName.of("option"), + Concatenation.of( + StringElement.of("["), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of("]") + ) + ), + Rule.of( + RuleName.of("char-val"), + Concatenation.of( + RuleReference.of(CoreRules.DQUOTE.asRuleName()), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21) + ), + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x23), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) + ) + ) + ) + ), + RuleReference.of(CoreRules.DQUOTE.asRuleName()) + ) + ), + Rule.of( + RuleName.of("num-val"), + Concatenation.of( + StringElement.of("%"), + SequenceGroup.of( + Alternative.of( + RuleReference.of(RuleName.of("bin-val")), + RuleReference.of(RuleName.of("dec-val")), + RuleReference.of(RuleName.of("hex-val")) + ) + ) + ) + ), + Rule.of( + RuleName.of("bin-val"), + Concatenation.of( + StringElement.of("b"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.BIT.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.BIT.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.BIT.asRuleName()), + 1 + ) + ) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("dec-val"), + Concatenation.of( + StringElement.of("d"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.DIGIT.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.DIGIT.asRuleName()), + 1 + ) + ) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("hex-val"), + Concatenation.of( + StringElement.of("x"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.HEXDIG.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.HEXDIG.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.HEXDIG.asRuleName()), + 1 + ) + ) + ) + ) + ) + ) + ) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/module-info.java b/vocabulary-format-assertion/src/test/java/module-info.java index dca81c0a..3c809450 100644 --- a/vocabulary-format-assertion/src/test/java/module-info.java +++ b/vocabulary-format-assertion/src/test/java/module-info.java @@ -25,6 +25,7 @@ open module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; requires io.github.sebastiantoepfer.jsonschema; + requires java.logging; requires jakarta.json; requires org.junit.jupiter.api; From 7bfff1244a339e54628da0f5aeb2a1c89e3c86de Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:42:50 +0100 Subject: [PATCH 04/11] make elements printable --- vocabulary-format-assertion/pom.xml | 14 ++ .../format/assertion/abnf/CoreRules.java | 170 ++++++++++-------- .../format/assertion/abnf/Rule.java | 9 +- .../format/assertion/abnf/RuleList.java | 9 +- .../assertion/abnf/element/Alternative.java | 6 + .../assertion/abnf/element/Concatenation.java | 6 + .../assertion/abnf/element/Element.java | 4 +- .../abnf/element/NumericCharacter.java | 6 + .../abnf/element/OptionalSequence.java | 6 + .../assertion/abnf/element/RuleName.java | 9 +- .../assertion/abnf/element/RuleReference.java | 6 + .../assertion/abnf/element/SequenceGroup.java | 6 + .../abnf/element/SpecificRepetition.java | 9 + .../assertion/abnf/element/StringElement.java | 6 + .../abnf/element/ValueRangeAlternatives.java | 6 + .../abnf/element/VariableRepetition.java | 16 ++ .../assertion/abnf/reader/TextABNF.java | 6 +- .../src/main/java/module-info.java | 1 + .../format/assertion/abnf/CoreRulesTest.java | 79 ++++++++ .../format/assertion/abnf/RuleListTest.java | 40 +++++ .../format/assertion/abnf/RuleTest.java | 33 ++++ .../abnf/element/ConcatenationTest.java | 22 +++ .../abnf/element/NumericCharacterTest.java | 16 ++ .../abnf/element/OptionalSequenceTest.java | 23 +++ .../abnf/element/RuleReferenceTest.java | 18 ++ .../abnf/element/SequenceGroupTest.java | 22 +++ .../abnf/element/SpecificRepetitionTest.java | 16 ++ .../element/ValueRangeAlternativesTest.java | 24 +++ .../abnf/element/VariableRepetitionTest.java | 57 ++++++ .../src/test/java/module-info.java | 2 + 30 files changed, 569 insertions(+), 78 deletions(-) diff --git a/vocabulary-format-assertion/pom.xml b/vocabulary-format-assertion/pom.xml index f91f7494..26db662f 100644 --- a/vocabulary-format-assertion/pom.xml +++ b/vocabulary-format-assertion/pom.xml @@ -14,12 +14,26 @@ json-schema-vocabulary-format-assertion Json Schema :: vocabulary :: format assertion + + 0.4.0 + + ${project.groupId} json-schema-vocabulary-spi ${project.version} + + io.github.sebastian-toepfer.ddd + common + ${ddd.verison} + + + io.github.sebastian-toepfer.ddd + media-core + ${ddd.verison} + org.junit.jupiter diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java index 58a629ce..aae23cfd 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java @@ -23,134 +23,155 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf; +import io.github.sebastiantoepfer.ddd.common.Media; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; public enum CoreRules implements Element { ALPHA() { @Override - public boolean isValidFor(final int codePoint) { - return Alternative - .of( - ValueRangeAlternatives.of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x41), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x5A) - ), - ValueRangeAlternatives.of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x61), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7A) - ) + Element definition() { + return Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x41), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x5A) + ), + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x61), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7A) ) - .isValidFor(codePoint); + ); } }, BIT() { @Override - public boolean isValidFor(final int codePoint) { - return Alternative.of(StringElement.of("0"), StringElement.of("1")).isValidFor(codePoint); + Element definition() { + return Alternative.of(StringElement.of("0"), StringElement.of("1")); } }, CHAR() { @Override - public boolean isValidFor(final int codePoint) { - return ValueRangeAlternatives - .of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x01), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7F) - ) - .isValidFor(codePoint); + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x01), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7F) + ); } }, CR() { @Override - public boolean isValidFor(final int codePoint) { - return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0D).isValidFor(codePoint); + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0D); + } + }, + CRLF() { + @Override + Element definition() { + return Concatenation.of(RuleReference.of(CR.asRuleName()), RuleReference.of(LF.asRuleName())); + } + }, + CTL() { + @Override + Element definition() { + return Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x00), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x1F) + ), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7F) + ); } }, - CRLF, //CR LF - CTL, //%x00-1F / %x7F DIGIT() { @Override - public boolean isValidFor(final int codePoint) { - return ValueRangeAlternatives - .of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x30), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x39) - ) - .isValidFor(codePoint); + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x30), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x39) + ); } }, DQUOTE() { @Override - public boolean isValidFor(final int codePoint) { - return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x22).isValidFor(codePoint); + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x22); } }, HEXDIG() { @Override - public boolean isValidFor(final int codePoint) { - return Alternative - .of( - DIGIT, - StringElement.of("A"), - StringElement.of("B"), - StringElement.of("C"), - StringElement.of("D"), - StringElement.of("E"), - StringElement.of("F") - ) - .isValidFor(codePoint); + Element definition() { + return Alternative.of( + DIGIT, + StringElement.of("A"), + StringElement.of("B"), + StringElement.of("C"), + StringElement.of("D"), + StringElement.of("E"), + StringElement.of("F") + ); } }, HTAB() { @Override - public boolean isValidFor(final int codePoint) { - return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x09).isValidFor(codePoint); + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x09); } }, LF() { @Override - public boolean isValidFor(final int codePoint) { - return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0A).isValidFor(codePoint); + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0A); } }, - LWSP, //*(WSP / CRLF WSP) - OCTET() { + LWSP() { @Override - public boolean isValidFor(final int codePoint) { - return ValueRangeAlternatives - .of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x00), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0xFF) + Element definition() { + return VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + Alternative.of(RuleReference.of(WSP.asRuleName()), RuleReference.of(CRLF.asRuleName())), + RuleReference.of(WSP.asRuleName()) + ) ) - .isValidFor(codePoint); + ); + } + }, + OCTET() { + @Override + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x00), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0xFF) + ); } }, SP() { @Override - public boolean isValidFor(final int codePoint) { - return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20).isValidFor(codePoint); + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20); } }, VCHAR() { @Override - public boolean isValidFor(final int codePoint) { - return ValueRangeAlternatives - .of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) - ) - .isValidFor(codePoint); + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) + ); } }, WSP { @Override - public boolean isValidFor(final int codePoint) { - return Alternative.of(SP, HTAB).isValidFor(codePoint); + Element definition() { + return Alternative.of(SP, HTAB); } }; @@ -159,7 +180,14 @@ public RuleName asRuleName() { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public final > T printOn(final T media) { + return Rule.of(asRuleName(), definition()).printOn(media).withValue("type", "corerule"); } + + @Override + public final boolean isValidFor(final int codePoint) { + return definition().isValidFor(codePoint); + } + + abstract Element definition(); } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java index 7645cba3..4bb978ec 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java @@ -23,11 +23,13 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf; +import io.github.sebastiantoepfer.ddd.common.Media; +import io.github.sebastiantoepfer.ddd.common.Printable; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; import java.util.Objects; -public final class Rule { +public final class Rule implements Printable { public static Rule of(final RuleName name, final Element elements) { return new Rule(name, elements); @@ -45,6 +47,11 @@ public boolean hasRuleName(final RuleName name) { return Objects.equals(this.name, name); } + @Override + public > T printOn(final T media) { + return media.withValue("name", name).withValue("type", "rule").withValue("elements", elements); + } + @Override public String toString() { return "Rule{" + "name=" + name + ", elements=" + elements + '}'; diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java index 10aa37ba..55dc6825 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java @@ -25,11 +25,13 @@ import static java.util.Arrays.asList; +import io.github.sebastiantoepfer.ddd.common.Media; +import io.github.sebastiantoepfer.ddd.common.Printable; import java.util.ArrayList; import java.util.List; import java.util.Objects; -public final class RuleList { +public final class RuleList implements Printable { public static RuleList of(final Rule rule, final Rule... rules) { final List ruleList = new ArrayList<>(); @@ -48,6 +50,11 @@ private RuleList(final List rules) { this.rules = List.copyOf(rules); } + @Override + public > T printOn(final T media) { + return media.withValue("rules", List.copyOf(rules)); + } + @Override public String toString() { return "RuleList{" + "rules=" + rules + '}'; diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java index a29d79ba..13ec2721 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -49,6 +50,11 @@ private Alternative(final Collection alternatives) { this.alternatives = List.copyOf(alternatives); } + @Override + public > T printOn(final T media) { + return media.withValue("type", "alternative").withValue("alternatives", List.copyOf(alternatives)); + } + @Override public boolean isValidFor(final int codePoint) { return alternatives.stream().anyMatch(e -> e.isValidFor(codePoint)); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java index 9a2be703..6aa48ec1 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -49,6 +50,11 @@ private Concatenation(final Collection concatenations) { this.concatenations = List.copyOf(concatenations); } + @Override + public > T printOn(final T media) { + return media.withValue("type", "concatenation").withValue("concatenations", List.copyOf(concatenations)); + } + @Override public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java index ad3999c7..8e84578e 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java @@ -23,7 +23,9 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; -public interface Element { +import io.github.sebastiantoepfer.ddd.common.Printable; + +public interface Element extends Printable { //no no, but let get start simple boolean isValidFor(int codePoint); } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java index 9db8237d..d4e870e4 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Arrays; import java.util.Objects; @@ -45,6 +46,11 @@ public boolean isValidFor(final int codePoint) { return value == codePoint; } + @Override + public > T printOn(final T media) { + return media.withValue("type", "num-val").withValue("base", base.name()).withValue("value", value); + } + boolean lessThanOrEquals(final int codePoint) { return value <= codePoint; } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java index 86ece7b8..818c41cc 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -43,6 +44,11 @@ public OptionalSequence(final Collection optionalElements) { this.optionalElements = List.copyOf(optionalElements); } + @Override + public > T printOn(final T media) { + return media.withValue("type", "option").withValue("optionals", List.copyOf(optionalElements)); + } + @Override public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java index 6261da81..7117b4e1 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java @@ -23,10 +23,12 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; +import io.github.sebastiantoepfer.ddd.common.Printable; import java.util.Locale; import java.util.Objects; -public final class RuleName { +public final class RuleName implements Printable { public static RuleName of(final String name) { return new RuleName(name); @@ -38,6 +40,11 @@ private RuleName(final String name) { this.name = Objects.requireNonNull(name); } + @Override + public > T printOn(final T media) { + return media.withValue("name", name).withValue("type", "rulename"); + } + @Override public int hashCode() { int hash = 7; diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java index 0d7bb644..bcefab1b 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Objects; public final class RuleReference implements Element { @@ -42,6 +43,11 @@ public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public > T printOn(final T media) { + return media.withValue("type", "rule-ref").withValue("name", name); + } + @Override public String toString() { return "RuleReference{" + "name=" + name + '}'; diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java index c6dbff11..44f9a82c 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -53,6 +54,11 @@ private SequenceGroup(final Collection elements) { this.elements = List.copyOf(elements); } + @Override + public > T printOn(final T media) { + return media.withValue("type", "group").withValue("elements", List.copyOf(elements)); + } + @Override public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java index f6eaade4..fce29ab3 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Objects; public final class SpecificRepetition implements Element { @@ -39,6 +40,14 @@ private SpecificRepetition(final Element elementToRepeat, final int occurences) this.occurences = occurences; } + @Override + public > T printOn(final T media) { + return media + .withValue("type", "fix-repetition") + .withValue("repeat", occurences) + .withValue("element", elementToRepeat); + } + @Override public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java index 96c7c96f..f3a156bd 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Locale; import java.util.Objects; @@ -49,6 +50,11 @@ public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public > T printOn(final T media) { + return media.withValue("type", "char-val").withValue("value", value); + } + @Override public int hashCode() { int hash = 7; diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java index 39e14ab2..c271c841 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Objects; public final class ValueRangeAlternatives implements Element { @@ -39,6 +40,11 @@ public ValueRangeAlternatives(final NumericCharacter start, final NumericCharact this.end = end; } + @Override + public > T printOn(final T media) { + return media.withValue("type", "val-range").withValue("from", start).withValue("to", end); + } + @Override public boolean isValidFor(final int codePoint) { return start.lessThanOrEquals(codePoint) && end.greatherThaneOrEquals(codePoint); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java index 02acd3ed..3cf86c23 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; +import io.github.sebastiantoepfer.ddd.common.Media; import java.util.Objects; public final class VariableRepetition implements Element { @@ -57,6 +58,21 @@ private VariableRepetition(final Element element, final int minOccurrences, fina this.element = Objects.requireNonNull(element); } + @Override + public > T printOn(final T media) { + final T result; + if (minOccurrences > 0 && maxOccurrences < Integer.MAX_VALUE) { + result = media.withValue("atLeast", minOccurrences).withValue("atMost", maxOccurrences); + } else if (minOccurrences > 0) { + result = media.withValue("atLeast", minOccurrences); + } else if (maxOccurrences < Integer.MAX_VALUE) { + result = media.withValue("atMost", maxOccurrences); + } else { + result = media; + } + return result.withValue("type", "repetition").withValue("element", element); + } + @Override public boolean isValidFor(final int codePoint) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java index 0461162c..ab57fa23 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java @@ -30,13 +30,13 @@ public final class TextABNF implements ABNF { private static final Logger LOG = Logger.getLogger(TextABNF.class.getName()); - public static TextABNF of(final String rules) { + public static TextABNF of(final CharSequence rules) { return new TextABNF(rules); } - private final String rules; + private final CharSequence rules; - private TextABNF(final String rules) { + private TextABNF(final CharSequence rules) { this.rules = rules; } diff --git a/vocabulary-format-assertion/src/main/java/module-info.java b/vocabulary-format-assertion/src/main/java/module-info.java index a0041318..a536ada1 100644 --- a/vocabulary-format-assertion/src/main/java/module-info.java +++ b/vocabulary-format-assertion/src/main/java/module-info.java @@ -24,6 +24,7 @@ module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; requires io.github.sebastiantoepfer.jsonschema; + requires io.github.sebastiantoepfer.ddd.common; requires java.logging; requires jakarta.json; diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java index 6b798214..cf5ee8d7 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java @@ -24,9 +24,14 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -34,6 +39,31 @@ class CoreRulesTest { + @Test + void should_be_printable() { + assertThat( + CoreRules.VCHAR.printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("corerule")), + hasEntry(is("name"), allOf(hasEntry(is("name"), is("VCHAR")), hasEntry(is("type"), is("rulename")))), + hasEntry( + is("elements"), + allOf( + hasEntry(is("type"), is("val-range")), + hasEntry( + is("from"), + allOf(hasEntry(is("base"), is("HEXADECIMAL")), (Matcher) hasEntry(is("value"), is(0x21))) + ), + hasEntry( + is("to"), + allOf(hasEntry(is("base"), is("HEXADECIMAL")), (Matcher) hasEntry(is("value"), is(0x7E))) + ) + ) + ) + ) + ); + } + @Nested class Alpha { @@ -114,6 +144,41 @@ void should_be_invalid_for_a() { } } + @Nested + class Crlf { + + @Test + void should_return_rulename() { + assertThat(CoreRules.CRLF.asRuleName(), is(RuleName.of("CRLF"))); + } + + @Test + void should_nit_be_supported() { + assertThrows(UnsupportedOperationException.class, () -> CoreRules.CRLF.isValidFor(0x0D)); + } + } + + @Nested + class Ctl { + + @Test + void should_return_rulename() { + assertThat(CoreRules.CTL.asRuleName(), is(RuleName.of("CTL"))); + } + + @ParameterizedTest(name = "{0} is a valid CTL") + @ValueSource(ints = { 0x00, 0x01, 0x11, 0x1A, 0x1F, 0x7F }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.CTL.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid CTL") + @ValueSource(ints = { 0x20, 0x30, 0x6A, 0x7A, 0x7E }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.CTL.isValidFor(codePoint), is(false)); + } + } + @Nested class Digit { @@ -242,6 +307,20 @@ void should_be_invalid_for_a() { } } + @Nested + class Lwsp { + + @Test + void should_return_rulename() { + assertThat(CoreRules.LWSP.asRuleName(), is(RuleName.of("LWSP"))); + } + + @Test + void should_nit_be_supported() { + assertThrows(UnsupportedOperationException.class, () -> CoreRules.LWSP.isValidFor(0x0D)); + } + } + @Nested class Octet { diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java index a9367d8d..90cf552d 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java @@ -24,13 +24,19 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class RuleListTest { @@ -44,4 +50,38 @@ void should_create_new() { void equalsContract() { EqualsVerifier.forClass(RuleList.class).verify(); } + + @Test + void should_be_printable() { + assertThat( + RuleList + .of(Rule.of(RuleName.of("rulename"), Alternative.of(StringElement.of("/"), StringElement.of(";")))) + .printOn(new HashMapMedia()), + (Matcher) hasEntry( + is("rules"), + contains( + allOf( + (Matcher) hasEntry(is("type"), is("rule")), + hasEntry( + is("name"), + allOf(hasEntry(is("name"), is("rulename")), hasEntry(is("type"), is("rulename"))) + ), + hasEntry( + is("elements"), + allOf( + (Matcher) hasEntry(is("type"), is("alternative")), + hasEntry( + is("alternatives"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ) + ) + ) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java index 8a1d89e7..124baa5c 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java @@ -24,11 +24,18 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class RuleTest { @@ -45,4 +52,30 @@ void should_know_his_name() { assertThat(rule.hasRuleName(RuleName.of("date")), is(true)); assertThat(rule.hasRuleName(RuleName.of("iso-date-time")), is(false)); } + + @Test + void should_be_printable() { + assertThat( + Rule + .of(RuleName.of("rulename"), Alternative.of(StringElement.of("/"), StringElement.of(";"))) + .printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("rule")), + hasEntry(is("name"), allOf(hasEntry(is("name"), is("rulename")), hasEntry(is("type"), is("rulename")))), + hasEntry( + is("elements"), + allOf( + (Matcher) hasEntry(is("type"), is("alternative")), + hasEntry( + is("alternatives"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java index 03a1aaf7..95606799 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java @@ -24,13 +24,18 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import java.util.List; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class ConcatenationTest { @@ -47,4 +52,21 @@ void should_be_created_an_equals_instance() { both(is(Concatenation.of(StringElement.of("a"), StringElement.of("b")))).and(not(nullValue())) ); } + + @Test + void should_be_printable() { + assertThat( + Concatenation.of(StringElement.of("/"), StringElement.of(";")).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("concatenation")), + hasEntry( + is("concatenations"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java index 71743f81..f82902c1 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java @@ -24,10 +24,14 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class NumericCharacterTest { @@ -66,4 +70,16 @@ void should_create_hex_base() { void should_throw_exception_for_invalid_shortname() { assertThrows(IllegalArgumentException.class, () -> NumericCharacter.BASE.findByShortName('h')); } + + @Test + void should_be_printable() { + assertThat( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10101).printOn(new HashMapMedia()), + allOf( + hasEntry(is("type"), is("num-val")), + hasEntry(is("base"), is("BINARY")), + (Matcher) hasEntry(is("value"), is(21)) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java index 5bb72885..4bd99697 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java @@ -24,11 +24,17 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import java.util.List; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class OptionalSequenceTest { @@ -42,4 +48,21 @@ void equalsContract() { void should_create_new_optionalsequence() { assertThat(OptionalSequence.of(StringElement.of(".")), is(not(nullValue()))); } + + @Test + void should_be_printable() { + assertThat( + OptionalSequence.of(List.of(StringElement.of("/"), StringElement.of(";"))).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("option")), + hasEntry( + is("optionals"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java index 28a2f3f6..37411d8d 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java @@ -24,11 +24,15 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class RuleReferenceTest { @@ -42,4 +46,18 @@ void should_create_new() { void equalsContract() { EqualsVerifier.forClass(RuleReference.class).verify(); } + + @Test + void should_be_printable() { + assertThat( + RuleReference.of(RuleName.of("test")).printOn(new HashMapMedia()), + allOf( + hasEntry(is("type"), is("rule-ref")), + (Matcher) hasEntry( + is("name"), + allOf(hasEntry(is("type"), is("rulename")), hasEntry(is("name"), is("test"))) + ) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java index d4882d40..9d758d8e 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java @@ -24,11 +24,16 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class SequenceGroupTest { @@ -49,4 +54,21 @@ void should_create() { is(not(nullValue())) ); } + + @Test + void should_be_printable() { + assertThat( + SequenceGroup.of(StringElement.of("/"), StringElement.of(";")).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("group")), + hasEntry( + is("elements"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java index ce10260e..f0b378c7 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java @@ -24,11 +24,15 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class SpecificRepetitionTest { @@ -42,4 +46,16 @@ void equalsContract() { void should_create_new_element() { assertThat(SpecificRepetition.of(StringElement.of("1"), 1), is(not(nullValue()))); } + + @Test + void should_be_printable() { + assertThat( + SpecificRepetition.of(StringElement.of("."), 2).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("fix-repetition")), + (Matcher) hasEntry(is("repeat"), is(2)), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java index d665fb77..a0fe1cfc 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java @@ -24,11 +24,15 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class ValueRangeAlternativesTest { @@ -113,4 +117,24 @@ void should_be_valid_if_value_is_maximum() { is(true) ); } + + @Test + void should_be_printable() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10101), + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10111) + ) + .printOn(new HashMapMedia()), + allOf( + hasEntry(is("type"), is("val-range")), + hasEntry( + is("from"), + allOf(hasEntry(is("base"), is("BINARY")), (Matcher) hasEntry(is("value"), is(21))) + ), + hasEntry(is("to"), allOf(hasEntry(is("base"), is("BINARY")), (Matcher) hasEntry(is("value"), is(23)))) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java index 747d491f..859f0146 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java @@ -24,11 +24,16 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; class VariableRepetitionTest { @@ -60,4 +65,56 @@ void should_create_new_at_least_element() { void should_create_new_optional_element() { assertThat(VariableRepetition.of(StringElement.of("1")), is(not(nullValue()))); } + + @Test + void should_be_printable() { + assertThat( + VariableRepetition.of(StringElement.of(".")).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + not(hasKey("atLeast")), + not(hasKey("atMost")), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_be_printable_with_atLeast() { + assertThat( + VariableRepetition.ofAtLeast(StringElement.of("."), 2).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + (Matcher) hasEntry(is("atLeast"), is(2)), + not(hasKey("atMost")), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_be_printable_with_atMost() { + assertThat( + VariableRepetition.ofAtMost(StringElement.of("."), 10).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + (Matcher) hasEntry(is("atMost"), is(10)), + not(hasKey("atLeast")), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_be_printable_with_between() { + assertThat( + VariableRepetition.ofBetween(StringElement.of("."), 2, 10).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + (Matcher) hasEntry(is("atLeast"), is(2)), + (Matcher) hasEntry(is("atMost"), is(10)), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/module-info.java b/vocabulary-format-assertion/src/test/java/module-info.java index 3c809450..79fd6c6b 100644 --- a/vocabulary-format-assertion/src/test/java/module-info.java +++ b/vocabulary-format-assertion/src/test/java/module-info.java @@ -25,9 +25,11 @@ open module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; requires io.github.sebastiantoepfer.jsonschema; + requires io.github.sebastiantoepfer.ddd.common; requires java.logging; requires jakarta.json; + requires io.github.sebastiantoepfer.ddd.media.core; requires org.junit.jupiter.api; requires org.junit.jupiter.params; requires org.hamcrest; From 0051bc3db0aded3fc51e709f1564ce66e288e770 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Thu, 16 Nov 2023 22:15:42 +0100 Subject: [PATCH 05/11] add support to read abnf from stream --- .../format/assertion/abnf/reader/ABNF.java | 2 +- .../format/assertion/abnf/reader/ABNFs.java | 46 ++ .../abnf/reader/StreambasedABNF.java | 70 ++++ .../assertion/abnf/reader/TextABNF.java | 7 +- .../assertion/abnf/reader/ABNFsTest.java | 393 ++++++++++++++++++ .../abnf/reader/StreambasedABNFTest.java | 58 +++ .../assertion/abnf/reader/TextABNFTest.java | 388 ----------------- .../src/test/resources/ABNFspec.txt | 25 ++ 8 files changed, 599 insertions(+), 390 deletions(-) create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java delete mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java create mode 100644 vocabulary-format-assertion/src/test/resources/ABNFspec.txt diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java index 3b2d3f30..0b72a9a2 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNF.java @@ -25,6 +25,6 @@ import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; -public interface ABNF { +public interface ABNF extends AutoCloseable { RuleList rules(); } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java new file mode 100644 index 00000000..32cbc47e --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +public final class ABNFs { + + public static ABNF of(final CharSequence sequence) { + return TextABNF.of(sequence); + } + + public static ABNF of(final InputStream is, final Charset cs) { + return of(new InputStreamReader(is, cs)); + } + + public static ABNF of(final Reader input) { + return StreambasedABNF.of(input); + } + + private ABNFs() {} +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java new file mode 100644 index 00000000..1f8c2214 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import java.io.IOException; +import java.io.Reader; +import java.util.Objects; +import java.util.logging.Logger; + +final class StreambasedABNF implements ABNF { + + public static ABNF of(final Reader reader) { + return new StreambasedABNF(reader); + } + + private static final Logger LOG = Logger.getLogger(StreambasedABNF.class.getName()); + + private final Reader reader; + + private StreambasedABNF(final Reader reader) { + this.reader = Objects.requireNonNull(reader); + } + + @Override + public void close() throws IOException { + reader.close(); + } + + @Override + public RuleList rules() { + LOG.entering(StreambasedABNF.class.getName(), "rules"); + final RuleList result; + try { + Extractor extractor = RuleListExtractor.of(); + int codePoint; + while ((codePoint = reader.read()) != -1) { + extractor = extractor.append(codePoint); + } + result = extractor.finish().createAs(RuleList.class); + } catch (IOException ex) { + final IllegalArgumentException thrown = new IllegalArgumentException(ex); + LOG.throwing(StreambasedABNF.class.getName(), "rules", thrown); + throw thrown; + } + LOG.exiting(TextABNF.class.getName(), "rules", result); + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java index ab57fa23..d4d4b85f 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java @@ -26,7 +26,7 @@ import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; import java.util.logging.Logger; -public final class TextABNF implements ABNF { +final class TextABNF implements ABNF { private static final Logger LOG = Logger.getLogger(TextABNF.class.getName()); @@ -40,6 +40,11 @@ private TextABNF(final CharSequence rules) { this.rules = rules; } + @Override + public void close() { + //nothing to do! + } + @Override public RuleList rules() { LOG.entering(TextABNF.class.getName(), "rules"); diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java new file mode 100644 index 00000000..ebf254d8 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java @@ -0,0 +1,393 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import java.nio.charset.StandardCharsets; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class ABNFsTest { + + @Test + void should_create_abnf_rules_from_string() { + //Note: a rule MUST end with crlf!! see rfc5234. lf is already set by the formatting :) + assertThat( + ABNFs + .of( + """ + rulelist = 1*( rule / (*c-wsp c-nl) )\r + rule = rulename defined-as elements c-nl\r + rulename = ALPHA *(ALPHA / DIGIT / "-")\r + defined-as = *c-wsp ("=" / "=/") *c-wsp\r + elements = alternation *c-wsp\r + c-wsp = WSP / (c-nl WSP)\r + c-nl = comment / CRLF\r + comment = ";" *(WSP / VCHAR) CRLF\r + alternation = concatenation\r + *(*c-wsp "/" *c-wsp concatenation)\r + concatenation = repetition *(1*c-wsp repetition)\r + repetition = [repeat] element\r + repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)\r + element = rulename / group / option /\r + char-val / num-val / prose-val\r + group = "(" *c-wsp alternation *c-wsp ")"\r + option = "[" *c-wsp alternation *c-wsp "]"\r + char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE\r + num-val = "%" (bin-val / dec-val / hex-val)\r + bin-val = "b" 1*BIT\r + [ 1*("." 1*BIT) / ("-" 1*BIT) ]\r + dec-val = "d" 1*DIGIT\r + [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]\r + hex-val = "x" 1*HEXDIG\r + [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]\r + """ + ) + .rules(), + isAbnfSpecAsRuleList() + ); + } + + @Test + void should_create_abnf_rules_from_inputstream() { + assertThat( + ABNFs.of(ABNFs.class.getClassLoader().getResourceAsStream("ABNFspec.txt"), StandardCharsets.UTF_8).rules(), + isAbnfSpecAsRuleList() + ); + } + + private static Matcher isAbnfSpecAsRuleList() { + return is( + RuleList.of( + Rule.of( + RuleName.of("rulelist"), + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Alternative.of( + RuleReference.of(RuleName.of("rule")), + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("c-nl")) + ) + ) + ) + ), + 1 + ) + ), + Rule.of( + RuleName.of("rule"), + Concatenation.of( + RuleReference.of(RuleName.of("rulename")), + RuleReference.of(RuleName.of("defined-as")), + RuleReference.of(RuleName.of("elements")), + RuleReference.of(RuleName.of("c-nl")) + ) + ), + Rule.of( + RuleName.of("rulename"), + Concatenation.of( + RuleReference.of(CoreRules.ALPHA.asRuleName()), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + RuleReference.of(CoreRules.ALPHA.asRuleName()), + RuleReference.of(CoreRules.DIGIT.asRuleName()), + StringElement.of("-") + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("defined-as"), + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + SequenceGroup.of(Alternative.of(StringElement.of("="), StringElement.of("=/"))), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) + ) + ), + Rule.of( + RuleName.of("elements"), + Concatenation.of( + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) + ) + ), + Rule.of( + RuleName.of("c-wsp"), + Alternative.of( + RuleReference.of(RuleName.of("WSP")), + SequenceGroup.of( + Concatenation.of( + RuleReference.of(RuleName.of("c-nl")), + RuleReference.of(CoreRules.WSP.asRuleName()) + ) + ) + ) + ), + Rule.of( + RuleName.of("c-nl"), + Alternative.of( + RuleReference.of(RuleName.of("comment")), + RuleReference.of(CoreRules.CRLF.asRuleName()) + ) + ), + Rule.of( + RuleName.of("comment"), + Concatenation.of( + StringElement.of(";"), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + RuleReference.of(CoreRules.WSP.asRuleName()), + RuleReference.of(CoreRules.VCHAR.asRuleName()) + ) + ) + ), + RuleReference.of(CoreRules.CRLF.asRuleName()) + ) + ), + Rule.of( + RuleName.of("alternation"), + Concatenation.of( + RuleReference.of(RuleName.of("concatenation")), + VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of("/"), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("concatenation")) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("concatenation"), + Concatenation.of( + RuleReference.of(RuleName.of("repetition")), + VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("c-wsp")), 1), + RuleReference.of(RuleName.of("repetition")) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("repetition"), + Concatenation.of( + OptionalSequence.of(RuleReference.of(RuleName.of("repeat"))), + RuleReference.of(RuleName.of("element")) + ) + ), + Rule.of( + RuleName.of("repeat"), + Alternative.of( + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())), + StringElement.of("*"), + VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())) + ) + ) + ) + ), + Rule.of( + RuleName.of("element"), + Alternative.of( + RuleReference.of(RuleName.of("rulename")), + RuleReference.of(RuleName.of("group")), + RuleReference.of(RuleName.of("option")), + RuleReference.of(RuleName.of("char-val")), + RuleReference.of(RuleName.of("num-val")), + RuleReference.of(RuleName.of("prose-val")) + ) + ), + Rule.of( + RuleName.of("group"), + Concatenation.of( + StringElement.of("("), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of(")") + ) + ), + Rule.of( + RuleName.of("option"), + Concatenation.of( + StringElement.of("["), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of("]") + ) + ), + Rule.of( + RuleName.of("char-val"), + Concatenation.of( + RuleReference.of(CoreRules.DQUOTE.asRuleName()), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21) + ), + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x23), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) + ) + ) + ) + ), + RuleReference.of(CoreRules.DQUOTE.asRuleName()) + ) + ), + Rule.of( + RuleName.of("num-val"), + Concatenation.of( + StringElement.of("%"), + SequenceGroup.of( + Alternative.of( + RuleReference.of(RuleName.of("bin-val")), + RuleReference.of(RuleName.of("dec-val")), + RuleReference.of(RuleName.of("hex-val")) + ) + ) + ) + ), + Rule.of( + RuleName.of("bin-val"), + Concatenation.of( + StringElement.of("b"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.BIT.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.BIT.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.BIT.asRuleName()), 1) + ) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("dec-val"), + Concatenation.of( + StringElement.of("d"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.DIGIT.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1) + ) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("hex-val"), + Concatenation.of( + StringElement.of("x"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.HEXDIG.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.HEXDIG.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.HEXDIG.asRuleName()), 1) + ) + ) + ) + ) + ) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java new file mode 100644 index 00000000..0c70f651 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.io.Reader; +import org.junit.jupiter.api.Test; + +class StreambasedABNFTest { + + @Test + void should_close_underlining_reader() throws Exception { + final ReaderImpl r = new ReaderImpl(); + + StreambasedABNF.of(r).close(); + + assertThat(r.isClosed, is(true)); + } + + private class ReaderImpl extends Reader { + + boolean isClosed = false; + + @Override + public int read(char[] chars, int i, int i1) throws IOException { + return -1; + } + + @Override + public void close() throws IOException { + isClosed = true; + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java deleted file mode 100644 index 13ee520c..00000000 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * The MIT License - * - * Copyright 2023 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.format.assertion.abnf.reader; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; -import org.junit.jupiter.api.Test; - -class TextABNFTest { - - @Test - void should_create_abnf_rules_from_string() { - //Note: a rule MUST end with crlf!! see rfc5234. lf is already set by the formatting :) - assertThat( - TextABNF - .of( - """ - rulelist = 1*( rule / (*c-wsp c-nl) )\r - rule = rulename defined-as elements c-nl\r - rulename = ALPHA *(ALPHA / DIGIT / "-")\r - defined-as = *c-wsp ("=" / "=/") *c-wsp\r - elements = alternation *c-wsp\r - c-wsp = WSP / (c-nl WSP)\r - c-nl = comment / CRLF\r - comment = ";" *(WSP / VCHAR) CRLF\r - alternation = concatenation\r - *(*c-wsp "/" *c-wsp concatenation)\r - concatenation = repetition *(1*c-wsp repetition)\r - repetition = [repeat] element\r - repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)\r - element = rulename / group / option /\r - char-val / num-val / prose-val\r - group = "(" *c-wsp alternation *c-wsp ")"\r - option = "[" *c-wsp alternation *c-wsp "]"\r - char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE\r - num-val = "%" (bin-val / dec-val / hex-val)\r - bin-val = "b" 1*BIT\r - [ 1*("." 1*BIT) / ("-" 1*BIT) ]\r - dec-val = "d" 1*DIGIT\r - [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]\r - hex-val = "x" 1*HEXDIG\r - [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]\r - """ - ) - .rules(), - is( - RuleList.of( - Rule.of( - RuleName.of("rulelist"), - VariableRepetition.ofAtLeast( - SequenceGroup.of( - Alternative.of( - RuleReference.of(RuleName.of("rule")), - SequenceGroup.of( - Concatenation.of( - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - RuleReference.of(RuleName.of("c-nl")) - ) - ) - ) - ), - 1 - ) - ), - Rule.of( - RuleName.of("rule"), - Concatenation.of( - RuleReference.of(RuleName.of("rulename")), - RuleReference.of(RuleName.of("defined-as")), - RuleReference.of(RuleName.of("elements")), - RuleReference.of(RuleName.of("c-nl")) - ) - ), - Rule.of( - RuleName.of("rulename"), - Concatenation.of( - RuleReference.of(CoreRules.ALPHA.asRuleName()), - VariableRepetition.of( - SequenceGroup.of( - Alternative.of( - RuleReference.of(CoreRules.ALPHA.asRuleName()), - RuleReference.of(CoreRules.DIGIT.asRuleName()), - StringElement.of("-") - ) - ) - ) - ) - ), - Rule.of( - RuleName.of("defined-as"), - Concatenation.of( - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - SequenceGroup.of(Alternative.of(StringElement.of("="), StringElement.of("=/"))), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) - ) - ), - Rule.of( - RuleName.of("elements"), - Concatenation.of( - RuleReference.of(RuleName.of("alternation")), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) - ) - ), - Rule.of( - RuleName.of("c-wsp"), - Alternative.of( - RuleReference.of(RuleName.of("WSP")), - SequenceGroup.of( - Concatenation.of( - RuleReference.of(RuleName.of("c-nl")), - RuleReference.of(CoreRules.WSP.asRuleName()) - ) - ) - ) - ), - Rule.of( - RuleName.of("c-nl"), - Alternative.of( - RuleReference.of(RuleName.of("comment")), - RuleReference.of(CoreRules.CRLF.asRuleName()) - ) - ), - Rule.of( - RuleName.of("comment"), - Concatenation.of( - StringElement.of(";"), - VariableRepetition.of( - SequenceGroup.of( - Alternative.of( - RuleReference.of(CoreRules.WSP.asRuleName()), - RuleReference.of(CoreRules.VCHAR.asRuleName()) - ) - ) - ), - RuleReference.of(CoreRules.CRLF.asRuleName()) - ) - ), - Rule.of( - RuleName.of("alternation"), - Concatenation.of( - RuleReference.of(RuleName.of("concatenation")), - VariableRepetition.of( - SequenceGroup.of( - Concatenation.of( - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - StringElement.of("/"), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - RuleReference.of(RuleName.of("concatenation")) - ) - ) - ) - ) - ), - Rule.of( - RuleName.of("concatenation"), - Concatenation.of( - RuleReference.of(RuleName.of("repetition")), - VariableRepetition.of( - SequenceGroup.of( - Concatenation.of( - VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("c-wsp")), 1), - RuleReference.of(RuleName.of("repetition")) - ) - ) - ) - ) - ), - Rule.of( - RuleName.of("repetition"), - Concatenation.of( - OptionalSequence.of(RuleReference.of(RuleName.of("repeat"))), - RuleReference.of(RuleName.of("element")) - ) - ), - Rule.of( - RuleName.of("repeat"), - Alternative.of( - VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), - SequenceGroup.of( - Concatenation.of( - VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())), - StringElement.of("*"), - VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())) - ) - ) - ) - ), - Rule.of( - RuleName.of("element"), - Alternative.of( - RuleReference.of(RuleName.of("rulename")), - RuleReference.of(RuleName.of("group")), - RuleReference.of(RuleName.of("option")), - RuleReference.of(RuleName.of("char-val")), - RuleReference.of(RuleName.of("num-val")), - RuleReference.of(RuleName.of("prose-val")) - ) - ), - Rule.of( - RuleName.of("group"), - Concatenation.of( - StringElement.of("("), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - RuleReference.of(RuleName.of("alternation")), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - StringElement.of(")") - ) - ), - Rule.of( - RuleName.of("option"), - Concatenation.of( - StringElement.of("["), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - RuleReference.of(RuleName.of("alternation")), - VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), - StringElement.of("]") - ) - ), - Rule.of( - RuleName.of("char-val"), - Concatenation.of( - RuleReference.of(CoreRules.DQUOTE.asRuleName()), - VariableRepetition.of( - SequenceGroup.of( - Alternative.of( - ValueRangeAlternatives.of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21) - ), - ValueRangeAlternatives.of( - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x23), - NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) - ) - ) - ) - ), - RuleReference.of(CoreRules.DQUOTE.asRuleName()) - ) - ), - Rule.of( - RuleName.of("num-val"), - Concatenation.of( - StringElement.of("%"), - SequenceGroup.of( - Alternative.of( - RuleReference.of(RuleName.of("bin-val")), - RuleReference.of(RuleName.of("dec-val")), - RuleReference.of(RuleName.of("hex-val")) - ) - ) - ) - ), - Rule.of( - RuleName.of("bin-val"), - Concatenation.of( - StringElement.of("b"), - VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.BIT.asRuleName()), 1), - OptionalSequence.of( - Alternative.of( - VariableRepetition.ofAtLeast( - SequenceGroup.of( - Concatenation.of( - StringElement.of("."), - VariableRepetition.ofAtLeast( - RuleReference.of(CoreRules.BIT.asRuleName()), - 1 - ) - ) - ), - 1 - ), - SequenceGroup.of( - Concatenation.of( - StringElement.of("-"), - VariableRepetition.ofAtLeast( - RuleReference.of(CoreRules.BIT.asRuleName()), - 1 - ) - ) - ) - ) - ) - ) - ), - Rule.of( - RuleName.of("dec-val"), - Concatenation.of( - StringElement.of("d"), - VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), - OptionalSequence.of( - Alternative.of( - VariableRepetition.ofAtLeast( - SequenceGroup.of( - Concatenation.of( - StringElement.of("."), - VariableRepetition.ofAtLeast( - RuleReference.of(CoreRules.DIGIT.asRuleName()), - 1 - ) - ) - ), - 1 - ), - SequenceGroup.of( - Concatenation.of( - StringElement.of("-"), - VariableRepetition.ofAtLeast( - RuleReference.of(CoreRules.DIGIT.asRuleName()), - 1 - ) - ) - ) - ) - ) - ) - ), - Rule.of( - RuleName.of("hex-val"), - Concatenation.of( - StringElement.of("x"), - VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.HEXDIG.asRuleName()), 1), - OptionalSequence.of( - Alternative.of( - VariableRepetition.ofAtLeast( - SequenceGroup.of( - Concatenation.of( - StringElement.of("."), - VariableRepetition.ofAtLeast( - RuleReference.of(CoreRules.HEXDIG.asRuleName()), - 1 - ) - ) - ), - 1 - ), - SequenceGroup.of( - Concatenation.of( - StringElement.of("-"), - VariableRepetition.ofAtLeast( - RuleReference.of(CoreRules.HEXDIG.asRuleName()), - 1 - ) - ) - ) - ) - ) - ) - ) - ) - ) - ); - } -} diff --git a/vocabulary-format-assertion/src/test/resources/ABNFspec.txt b/vocabulary-format-assertion/src/test/resources/ABNFspec.txt new file mode 100644 index 00000000..3a5ffd54 --- /dev/null +++ b/vocabulary-format-assertion/src/test/resources/ABNFspec.txt @@ -0,0 +1,25 @@ +rulelist = 1*( rule / (*c-wsp c-nl) ) +rule = rulename defined-as elements c-nl +rulename = ALPHA *(ALPHA / DIGIT / "-") +defined-as = *c-wsp ("=" / "=/") *c-wsp +elements = alternation *c-wsp +c-wsp = WSP / (c-nl WSP) +c-nl = comment / CRLF +comment = ";" *(WSP / VCHAR) CRLF +alternation = concatenation + *(*c-wsp "/" *c-wsp concatenation) +concatenation = repetition *(1*c-wsp repetition) +repetition = [repeat] element +repeat = 1*DIGIT / (*DIGIT "*" *DIGIT) +element = rulename / group / option / + char-val / num-val / prose-val +group = "(" *c-wsp alternation *c-wsp ")" +option = "[" *c-wsp alternation *c-wsp "]" +char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE +num-val = "%" (bin-val / dec-val / hex-val) +bin-val = "b" 1*BIT + [ 1*("." 1*BIT) / ("-" 1*BIT) ] +dec-val = "d" 1*DIGIT + [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ] +hex-val = "x" 1*HEXDIG + [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ] From b89fdcd54453ad97d7ebee92ba7b3c593e58dca3 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:30:50 +0100 Subject: [PATCH 06/11] perpare code to alternative java21 variant --- .../abnf/reader/ConcatenationExtractor.java | 12 ++-- .../abnf/reader/ConcationTypeSwitch.java | 56 +++++++++++++++++++ .../abnf/reader/ExtractorFactory.java | 31 ++++++++++ .../abnf/reader/ExtractorOwnerFactory.java | 32 +++++++++++ 4 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java index 0b43c482..51a6db55 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java @@ -65,14 +65,12 @@ public Extractor append(final int codePoint) { } else if (Character.isWhitespace(codePoint)) { result = new ConcatenationExtractor(owner, elements, currentEndOfElementDetector); } else if (codePoint == SOLIDUS) { - final List newElements = new ArrayList<>(elements); - final Element element = newElements.remove(newElements.size() - 1); result = - AlternativeExtractor.of( - new ConcatenationExtractor(owner, newElements, currentEndOfElementDetector), - element, - currentEndOfElementDetector - ); + new ConcationTypeSwitch(owner, currentEndOfElementDetector, elements) + .switchTo( + (o, d, e) -> new ConcatenationExtractor(o, e, d), + (o, d, e) -> AlternativeExtractor.of(o, e, d) + ); } else { result = RepetitionExtractor.of(this).append(codePoint); } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java new file mode 100644 index 00000000..40f517ca --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java @@ -0,0 +1,56 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +final class ConcationTypeSwitch { + + private final ExtractorOwner owner; + private final ElementEndDetector endOfElementDetector; + private final List elements; + + public ConcationTypeSwitch( + final ExtractorOwner owner, + final ElementEndDetector endOfElementDetector, + final List elements + ) { + this.owner = Objects.requireNonNull(owner); + this.endOfElementDetector = Objects.requireNonNull(endOfElementDetector); + this.elements = List.copyOf(elements); + } + + public Extractor switchTo(final ExtractorOwnerFactory ownerFactory, final ExtractorFactory targetFactory) { + final List newElements = new ArrayList<>(elements); + final Element element = newElements.remove(newElements.size() - 1); + return targetFactory.create( + ownerFactory.create(owner, endOfElementDetector, newElements), + endOfElementDetector, + element + ); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java new file mode 100644 index 00000000..b918e195 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java @@ -0,0 +1,31 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; + +@FunctionalInterface +interface ExtractorFactory { + Extractor create(ExtractorOwner owner, ElementEndDetector currentEndOfElementDetector, Element element); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java new file mode 100644 index 00000000..87398bd4 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java @@ -0,0 +1,32 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.List; + +@FunctionalInterface +interface ExtractorOwnerFactory { + ExtractorOwner create(ExtractorOwner owner, ElementEndDetector currentEndOfElementDetector, List elements); +} From 23cf384daacbbc98609590090b916f8e909a8823 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:31:08 +0100 Subject: [PATCH 07/11] add java21 variant --- .../abnf/reader/ConcationTypeSwitch.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java diff --git a/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java b/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java new file mode 100644 index 00000000..5fc7a5db --- /dev/null +++ b/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java @@ -0,0 +1,56 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +class ConcationTypeSwitch { + + private final ExtractorOwner owner; + private final ElementEndDetector endOfElementDetector; + private final List elements; + + public ConcationTypeSwitch( + final ExtractorOwner owner, + final ElementEndDetector endOfElementDetector, + final List elements + ) { + this.owner = Objects.requireNonNull(owner); + this.endOfElementDetector = Objects.requireNonNull(endOfElementDetector); + this.elements = List.copyOf(elements); + } + + public Extractor switchTo(final ExtractorOwnerFactory ownerFactory, final ExtractorFactory targetFactory) { + final List newElements = new ArrayList<>(elements); + final Element element = newElements.removeLast(); + return targetFactory.create( + ownerFactory.create(owner, endOfElementDetector, newElements), + endOfElementDetector, + element + ); + } +} From 5a0fd4f7ca7c26f6bc67b8268705019ba920d9a1 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:09:03 +0100 Subject: [PATCH 08/11] improve isValidFor --- .../format/assertion/abnf/CoreRules.java | 27 ++-- .../assertion/abnf/element/Alternative.java | 7 +- .../assertion/abnf/element/Concatenation.java | 22 +++- .../assertion/abnf/element/Dimension.java | 122 ++++++++++++++++++ .../assertion/abnf/element/Element.java | 7 +- .../abnf/element/NumericCharacter.java | 12 +- .../abnf/element/OptionalSequence.java | 13 +- .../assertion/abnf/element/RuleName.java | 4 + .../assertion/abnf/element/RuleReference.java | 13 +- .../assertion/abnf/element/SequenceGroup.java | 21 ++- .../abnf/element/SpecificRepetition.java | 20 ++- .../assertion/abnf/element/StringElement.java | 20 +-- .../abnf/element/ValidateableCodePoint.java | 107 +++++++++++++++ .../abnf/element/ValueRangeAlternatives.java | 2 +- .../abnf/element/VariableRepetition.java | 25 +++- .../abnf/reader/NumValExtractor.java | 5 +- .../format/assertion/abnf/CoreRulesTest.java | 42 +++++- .../abnf/element/AlternativeTest.java | 11 +- .../abnf/element/ConcatenationTest.java | 47 ++++++- .../assertion/abnf/element/DimensionTest.java | 83 ++++++++++++ .../assertion/abnf/element/ElementTest.java | 51 ++++++++ .../abnf/element/OptionalSequenceTest.java | 18 +++ .../abnf/element/RuleReferenceTest.java | 26 ++++ .../abnf/element/SequenceGroupTest.java | 45 +++++++ .../abnf/element/SpecificRepetitionTest.java | 48 +++++++ .../abnf/element/StringElementTest.java | 31 ++++- .../element/ValidateableCodePointTest.java | 59 +++++++++ .../element/ValueRangeAlternativesTest.java | 23 +++- .../abnf/element/VariableRepetitionTest.java | 50 +++++++ 29 files changed, 884 insertions(+), 77 deletions(-) create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java index aae23cfd..0c9a0016 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java @@ -26,12 +26,13 @@ import io.github.sebastiantoepfer.ddd.common.Media; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Dimension; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; @@ -75,7 +76,7 @@ Element definition() { CRLF() { @Override Element definition() { - return Concatenation.of(RuleReference.of(CR.asRuleName()), RuleReference.of(LF.asRuleName())); + return Concatenation.of(CR, LF); } }, CTL() { @@ -134,14 +135,7 @@ Element definition() { LWSP() { @Override Element definition() { - return VariableRepetition.of( - SequenceGroup.of( - Concatenation.of( - Alternative.of(RuleReference.of(WSP.asRuleName()), RuleReference.of(CRLF.asRuleName())), - RuleReference.of(WSP.asRuleName()) - ) - ) - ); + return VariableRepetition.of(SequenceGroup.of(Concatenation.of(Alternative.of(WSP, CRLF), WSP))); } }, OCTET() { @@ -175,7 +169,7 @@ Element definition() { } }; - public RuleName asRuleName() { + public final RuleName asRuleName() { return RuleName.of(name()); } @@ -184,10 +178,19 @@ public final > T printOn(final T media) { return Rule.of(asRuleName(), definition()).printOn(media).withValue("type", "corerule"); } - @Override public final boolean isValidFor(final int codePoint) { + return isValidFor(ValidateableCodePoint.of(0, codePoint)); + } + + @Override + public final boolean isValidFor(final ValidateableCodePoint codePoint) { return definition().isValidFor(codePoint); } + @Override + public final Dimension dimension() { + return definition().dimension(); + } + abstract Element definition(); } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java index 13ec2721..87efed65 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java @@ -56,10 +56,15 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { + public boolean isValidFor(final ValidateableCodePoint codePoint) { return alternatives.stream().anyMatch(e -> e.isValidFor(codePoint)); } + @Override + public Dimension dimension() { + return alternatives.stream().map(Element::dimension).sorted().reduce(Dimension::expandTo).orElseThrow(); + } + @Override public int hashCode() { int hash = 7; diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java index 6aa48ec1..01512afd 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java @@ -56,8 +56,26 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public Dimension dimension() { + return concatenations.stream().map(Element::dimension).reduce(Dimension::plus).orElseThrow(); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + final Element first = concatenations.get(0); + if (first.dimension().isInRange(codePoint)) { + result = first.isValidFor(codePoint); + } else { + result = + of(concatenations.subList(1, concatenations.size())) + .isValidFor(codePoint.repositionBackBy(first.dimension())); + } + } else { + result = false; + } + return result; } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java new file mode 100644 index 00000000..bcb9e197 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static java.util.Comparator.comparingLong; + +public final class Dimension implements Comparable { + + private static final Dimension ZERO = Dimension.of(0); + private static final Dimension ONE = Dimension.of(1); + + public static Dimension zero() { + return ZERO; + } + + public static Dimension one() { + return ONE; + } + + public static Dimension of(final long size) { + return of(size, size); + } + + public static Dimension of(final long minSize, final long maxSize) { + if (minSize < 0) { + throw new IllegalArgumentException("minSize must be greather or equals than 0!"); + } + if (maxSize < minSize) { + throw new IllegalArgumentException("MaxSize must be greater or equals minSize!"); + } + return new Dimension(minSize, maxSize); + } + + private final long minSize; + private final long maxSize; + + private Dimension(final long minSize, final long maxSize) { + this.minSize = minSize; + this.maxSize = maxSize; + } + + public boolean isInRange(final ValidateableCodePoint codepoint) { + return codepoint.position() < maxSize; + } + + Dimension expandTo(final Dimension newMax) { + return of(Math.min(minSize, newMax.minSize), Math.max(maxSize, newMax.maxSize)); + } + + Dimension plus(final Dimension toAdd) { + return of(minSize + toAdd.minSize, maxSize + toAdd.maxSize); + } + + Dimension multipliesBy(final int multiplier) { + return of(minSize * multiplier, maxSize * multiplier); + } + + @Override + public int compareTo(final Dimension t) { + return comparingLong(Dimension::length).thenComparing(comparingLong(Dimension::minSize)).compare(this, t); + } + + private long minSize() { + return minSize; + } + + long length() { + return maxSize; + } + + @Override + public String toString() { + return "Dimension{" + "minSize=" + minSize + ", maxSize=" + maxSize + '}'; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + (int) (this.minSize ^ (this.minSize >>> 32)); + hash = 53 * hash + (int) (this.maxSize ^ (this.maxSize >>> 32)); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Dimension other = (Dimension) obj; + if (this.minSize != other.minSize) { + return false; + } + return this.maxSize == other.maxSize; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java index 8e84578e..efb83da4 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java @@ -26,6 +26,9 @@ import io.github.sebastiantoepfer.ddd.common.Printable; public interface Element extends Printable { - //no no, but let get start simple - boolean isValidFor(int codePoint); + boolean isValidFor(ValidateableCodePoint codePoint); + + default Dimension dimension() { + return Dimension.one(); + } } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java index d4e870e4..861f4ecb 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java @@ -42,8 +42,8 @@ private NumericCharacter(final BASE base, final int value) { } @Override - public boolean isValidFor(final int codePoint) { - return value == codePoint; + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return codePoint.isEqualsTo(value); } @Override @@ -51,12 +51,12 @@ public > T printOn(final T media) { return media.withValue("type", "num-val").withValue("base", base.name()).withValue("value", value); } - boolean lessThanOrEquals(final int codePoint) { - return value <= codePoint; + boolean lessThanOrEquals(final ValidateableCodePoint codePoint) { + return codePoint.isGreaterOrEqualsThan(value); } - boolean greatherThaneOrEquals(final int codePoint) { - return value >= codePoint; + boolean greatherThaneOrEquals(final ValidateableCodePoint codePoint) { + return codePoint.isLessOrEqualsThan(value); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java index 818c41cc..34de4c98 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java @@ -50,8 +50,17 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return true; + } + + @Override + public Dimension dimension() { + return Dimension + .zero() + .expandTo( + optionalElements.stream().map(Element::dimension).sorted().reduce(Dimension.zero(), Dimension::plus) + ); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java index 7117b4e1..24c7170f 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java @@ -40,6 +40,10 @@ private RuleName(final String name) { this.name = Objects.requireNonNull(name); } + String name() { + return name; + } + @Override public > T printOn(final T media) { return media.withValue("name", name).withValue("type", "rulename"); diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java index bcefab1b..6703388c 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java @@ -39,8 +39,17 @@ private RuleReference(final RuleName name) { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return ruleNameAsStringElement().isValidFor(codePoint); + } + + @Override + public Dimension dimension() { + return ruleNameAsStringElement().dimension(); + } + + Element ruleNameAsStringElement() { + return StringElement.of(name.name()); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java index 44f9a82c..4518b89a 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java @@ -60,8 +60,25 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + final Element first = elements.get(0); + if (first.dimension().isInRange(codePoint)) { + result = first.isValidFor(codePoint); + } else { + result = + of(elements.subList(1, elements.size())).isValidFor(codePoint.repositionBackBy(first.dimension())); + } + } else { + result = false; + } + return result; + } + + @Override + public Dimension dimension() { + return elements.stream().map(Element::dimension).reduce(Dimension.zero(), Dimension::plus); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java index fce29ab3..7b537758 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java @@ -49,8 +49,24 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + if (elementToRepeat.dimension().isInRange(codePoint)) { + result = elementToRepeat.isValidFor(codePoint); + } else { + result = + of(elementToRepeat, occurences).isValidFor(codePoint.repositionBackBy(elementToRepeat.dimension())); + } + } else { + result = false; + } + return result; + } + + @Override + public Dimension dimension() { + return elementToRepeat.dimension().multipliesBy(occurences); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java index f3a156bd..6ef3cb5f 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java @@ -40,14 +40,18 @@ private StringElement(final String value) { } @Override - public boolean isValidFor(final int codePoint) { - if (value.length() == 1) { - return ( - value.codePointAt(0) == Character.toUpperCase(codePoint) || - value.codePointAt(0) == Character.toLowerCase(codePoint) - ); - } - throw new UnsupportedOperationException("Not supported yet."); + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return ( + dimension().isInRange(codePoint) && + (codePoint.isEqualsTo(value.codePointAt(codePoint.position())) || + codePoint.isUpperCaseOf(value.codePointAt(codePoint.position())) || + codePoint.isLowerCaseOf(value.codePointAt(codePoint.position()))) + ); + } + + @Override + public Dimension dimension() { + return Dimension.of(value.length()); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java new file mode 100644 index 00000000..4e34365a --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java @@ -0,0 +1,107 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +public final class ValidateableCodePoint { + + public static ValidateableCodePoint of(final int position, final int value) { + if (position < 0) { + throw new IllegalArgumentException("position must be greather than 0!"); + } + if (value < 0) { + throw new IllegalArgumentException("value must be greather than 0!"); + } + return new ValidateableCodePoint(position, value); + } + + private final int position; + private final int value; + + private ValidateableCodePoint(final int position, final int value) { + this.position = position; + this.value = value; + } + + public ValidateableCodePoint repositionBackBy(final Dimension before) { + return repositionTo(position - (int) before.length()); + } + + public ValidateableCodePoint repositionBackBy(final int offset) { + return repositionTo(position - offset); + } + + public ValidateableCodePoint repositionTo(final int newPosition) { + return of(newPosition, value); + } + + public int position() { + return position; + } + + public boolean isEqualsTo(final int codePoint) { + return codePoint == value; + } + + public boolean isUpperCaseOf(final int codePoint) { + return Character.toUpperCase(codePoint) == value; + } + + public boolean isLowerCaseOf(final int codePoint) { + return Character.toLowerCase(codePoint) == value; + } + + public boolean isGreaterOrEqualsThan(final int codePoint) { + return codePoint <= value; + } + + public boolean isLessOrEqualsThan(final int codePoint) { + return value <= codePoint; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 71 * hash + this.position; + hash = 71 * hash + this.value; + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ValidateableCodePoint other = (ValidateableCodePoint) obj; + if (this.position != other.position) { + return false; + } + return this.value == other.value; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java index c271c841..4538f367 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java @@ -46,7 +46,7 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { + public boolean isValidFor(final ValidateableCodePoint codePoint) { return start.lessThanOrEquals(codePoint) && end.greatherThaneOrEquals(codePoint); } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java index 3cf86c23..ba998dc0 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java @@ -74,8 +74,29 @@ public > T printOn(final T media) { } @Override - public boolean isValidFor(final int codePoint) { - throw new UnsupportedOperationException("Not supported yet."); + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + if (element.dimension().isInRange(codePoint)) { + result = element.isValidFor(codePoint); + } else { + final int newMinOccurrences = Math.min(minOccurrences, 1); + result = + ofBetween(element, newMinOccurrences, Math.max(newMinOccurrences, maxOccurrences - 1)) + .isValidFor(codePoint.repositionBackBy(element.dimension())); + } + } else { + result = false; + } + return result; + } + + @Override + public Dimension dimension() { + return element + .dimension() + .multipliesBy(minOccurrences) + .expandTo(element.dimension().multipliesBy(maxOccurrences)); } @Override diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java index 4d99589c..888ab417 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java @@ -25,6 +25,7 @@ import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; import java.util.Objects; @@ -85,7 +86,7 @@ public Extractor append(final int codePoint) { final Extractor result; if (codePoint == UsefulCodepoints.HYPHEN_MINUS) { result = new ValueRangeExtractor(owner, base, rule, value); - } else if (Character.isWhitespace(codePoint) || !rule.isValidFor(codePoint)) { + } else if (Character.isWhitespace(codePoint) || !rule.isValidFor(ValidateableCodePoint.of(0, codePoint))) { result = owner.imDone(asCreator()).append(codePoint); } else { result = new SpecificNumValExtractor(owner, base, rule, value.concat(Character.toString(codePoint))); @@ -142,7 +143,7 @@ private ValueRangeExtractor( @Override public Extractor append(final int codePoint) { final Extractor result; - if (Character.isWhitespace(codePoint) || !rule.isValidFor(codePoint)) { + if (Character.isWhitespace(codePoint) || !rule.isValidFor(ValidateableCodePoint.of(0, codePoint))) { result = owner.imDone(asCreator()).append(codePoint); } else { result = new ValueRangeExtractor(owner, base, rule, start, end.concat(Character.toString(codePoint))); diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java index cf5ee8d7..829de2f8 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java @@ -27,10 +27,11 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertThrows; import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; import org.hamcrest.Matcher; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -153,8 +154,11 @@ void should_return_rulename() { } @Test - void should_nit_be_supported() { - assertThrows(UnsupportedOperationException.class, () -> CoreRules.CRLF.isValidFor(0x0D)); + void should_be_valid_for_crlf() { + final Element element = CoreRules.CRLF; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, '\r')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, '\n')), is(true)); } } @@ -316,8 +320,36 @@ void should_return_rulename() { } @Test - void should_nit_be_supported() { - assertThrows(UnsupportedOperationException.class, () -> CoreRules.LWSP.isValidFor(0x0D)); + void should_be_valid_for_sp_sp() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 0x20)), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 0x20)), is(true)); + } + + @Test + void should_be_valid_for_sp_htab() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 0x20)), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 0x09)), is(true)); + } + + @Test + void should_be_valid_for_htab_sp() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 0x09)), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 0x20)), is(true)); + } + + @Test + void should_be_valid_for_crlf_sp() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, '\r')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, '\n')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(2, 0x20)), is(true)); } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java index 060ee140..12cea960 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java @@ -24,12 +24,8 @@ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import java.util.List; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; @@ -41,10 +37,7 @@ void equalsContract() { } @Test - void should_be_created_an_equals_instance() { - assertThat( - Alternative.of(List.of(StringElement.of("a"), StringElement.of("b"))), - both(is(Alternative.of(StringElement.of("a"), StringElement.of("b")))).and(not(nullValue())) - ); + void should_return_dimension_from_smallest_and_larges() { + assertThat(Alternative.of(StringElement.of("ab"), StringElement.of("cde")).dimension(), is(Dimension.of(2, 3))); } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java index 95606799..f6911ea8 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java @@ -25,15 +25,11 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; -import java.util.List; import nl.jqno.equalsverifier.EqualsVerifier; import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; @@ -46,11 +42,48 @@ void equalsContract() { } @Test - void should_be_created_an_equals_instance() { + void should_return_dimension_as_sum() { assertThat( - Concatenation.of(List.of(StringElement.of("a"), StringElement.of("b"))), - both(is(Concatenation.of(StringElement.of("a"), StringElement.of("b")))).and(not(nullValue())) + Concatenation + .of( + Alternative.of(StringElement.of("ab"), StringElement.of("cde")), + Alternative.of(StringElement.of("x"), StringElement.of("yz")) + ) + .dimension(), + is(Dimension.of(3, 5)) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position() { + final Concatenation concatenation = Concatenation.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(0, 'a')), is(true)); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(1, 'b')), is(true)); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(2, 'c')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + final Concatenation concatenation = Concatenation.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(3, 'c')), is(false)); + } + + @Test + void should_be_invalid_if_codepoint_is_notequals_codepoint_at_position() { + final Concatenation concatenation = Concatenation.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') ); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(1, 'B')), is(false)); } @Test diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java new file mode 100644 index 00000000..49d13bac --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; + +import java.util.List; +import java.util.TreeSet; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class DimensionTest { + + @Test + void should_equals_contract() { + EqualsVerifier.forClass(Dimension.class).verify(); + } + + @Test + void should_know_if_codepoint_is_in_range() { + assertThat(Dimension.of(1).isInRange(ValidateableCodePoint.of(0, 0x13)), is(true)); + } + + @Test + void should_know_if_codepoint_is_not_in_range() { + assertThat(Dimension.of(1).isInRange(ValidateableCodePoint.of(1, 0x13)), is(false)); + } + + @Test + void should_create_expanded_upper_bondary() { + assertThat(Dimension.of(2).expandTo(Dimension.of(3, 7)), is(Dimension.of(2, 7))); + } + + @Test + void should_create_expanded_lower_and_upper_bondary() { + assertThat(Dimension.of(2).expandTo(Dimension.of(1, 7)), is(Dimension.of(1, 7))); + } + + @Test + void should_create_expanded_lower_bondary() { + assertThat(Dimension.of(2, 8).expandTo(Dimension.of(1, 7)), is(Dimension.of(1, 8))); + } + + @Test + void should_be_sortable() { + assertThat( + new TreeSet<>( + List.of( + Dimension.of(6), + Dimension.of(2, 4), + Dimension.of(2), + Dimension.of(1, 7), + Dimension.of(2, 4), + Dimension.of(2, 6) + ) + ), + contains(Dimension.of(2), Dimension.of(2, 4), Dimension.of(2, 6), Dimension.of(6), Dimension.of(1, 7)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java new file mode 100644 index 00000000..2e335dee --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.ddd.common.Media; +import org.junit.jupiter.api.Test; + +class ElementTest { + + @Test + void should_had_dimension_of_one_by_default() { + assertThat(new TestElement().dimension(), is(Dimension.of(1))); + } + + private static class TestElement implements Element { + + @Override + public boolean isValidFor(ValidateableCodePoint codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public > T printOn(T media) { + throw new UnsupportedOperationException("Not supported yet."); + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java index 4bd99697..500abb78 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java @@ -49,6 +49,14 @@ void should_create_new_optionalsequence() { assertThat(OptionalSequence.of(StringElement.of(".")), is(not(nullValue()))); } + @Test + void should_has_dimension_from_zero_to_max() { + assertThat( + OptionalSequence.of(List.of(StringElement.of("a"), StringElement.of("b"))).dimension(), + is(Dimension.of(0, 2)) + ); + } + @Test void should_be_printable() { assertThat( @@ -65,4 +73,14 @@ void should_be_printable() { ) ); } + + @Test + void should_be_valid_for_every_codepoint() { + assertThat( + OptionalSequence + .of(List.of(StringElement.of("/"), StringElement.of(";"))) + .isValidFor(ValidateableCodePoint.of(0, ',')), + is(true) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java index 37411d8d..10478e86 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java @@ -47,6 +47,11 @@ void equalsContract() { EqualsVerifier.forClass(RuleReference.class).verify(); } + @Test + void should_has_dimension_of_rulename_length() { + assertThat(RuleReference.of(RuleName.of("rule")).dimension(), is(Dimension.of(4))); + } + @Test void should_be_printable() { assertThat( @@ -60,4 +65,25 @@ void should_be_printable() { ) ); } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position() { + final Element element = RuleReference.of(RuleName.of("test")); + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 't')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 'e')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(2, 's')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(3, 't')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_not_equals_codepoint_at_position() { + final Element element = RuleReference.of(RuleName.of("test")); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 't')), is(false)); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + final Element element = RuleReference.of(RuleName.of("test")); + assertThat(element.isValidFor(ValidateableCodePoint.of(4, 't')), is(false)); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java index 9d758d8e..fdcd4ec8 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java @@ -55,6 +55,51 @@ void should_create() { ); } + @Test + void should_return_dimension_as_sum() { + assertThat( + SequenceGroup + .of( + Alternative.of(StringElement.of("ab"), StringElement.of("cde")), + Alternative.of(StringElement.of("x"), StringElement.of("yz")) + ) + .dimension(), + is(Dimension.of(3, 5)) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position() { + final Element element = SequenceGroup.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 'a')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 'b')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(2, 'c')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + final Element element = SequenceGroup.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(element.isValidFor(ValidateableCodePoint.of(3, 'c')), is(false)); + } + + @Test + void should_be_invalid_if_codepoint_is_notequals_codepoint_at_position() { + final Element element = SequenceGroup.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 'B')), is(false)); + } + @Test void should_be_printable() { assertThat( diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java index f0b378c7..3c5f9cfd 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java @@ -47,6 +47,54 @@ void should_create_new_element() { assertThat(SpecificRepetition.of(StringElement.of("1"), 1), is(not(nullValue()))); } + @Test + void should_dimension_as_multimple_of() { + assertThat( + SpecificRepetition.of(Alternative.of(StringElement.of("ab"), StringElement.of("cde")), 2).dimension(), + is(Dimension.of(4, 6)) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position_on_first_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(0, '1')), + is(true) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position_on_any_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(1, '1')), + is(true) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position_on_last_posible_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(2, '1')), + is(true) + ); + } + + @Test + void should_be_invalid_if_codepoint_is_notequals_codepoint_at_position_on_any_posible_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(1, '2')), + is(false) + ); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(3, '1')), + is(false) + ); + } + @Test void should_be_printable() { assertThat( diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java index f3ef17db..249ac865 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java @@ -27,7 +27,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; -import static org.junit.jupiter.api.Assertions.assertThrows; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; @@ -45,23 +44,41 @@ void equalsContract() { } @Test - void should_throw_execption_if_string_has_more_than_one_character() { - final StringElement element = StringElement.of("invalid"); - assertThrows(UnsupportedOperationException.class, () -> element.isValidFor(0x01)); + void should_return_string_length_as_dimension() { + assertThat(StringElement.of("abc").dimension(), is(Dimension.of(3))); + } + + @Test + void should_be_invalid_if_codepoint_is_outside_the_dimension() { + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(3, 'a')), is(false)); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_in_string_at_position() { + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(0, 'a')), is(true)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(1, 'b')), is(true)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(2, 'c')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_not_equals_codepoint_in_string_at_position() { + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(0, 'b')), is(false)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(1, 'a')), is(false)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(2, 'b')), is(false)); } @Test void should_be_valid_if_codePoint_is_lowercase_representation() { - assertThat(StringElement.of("A").isValidFor(0x61), is(true)); + assertThat(StringElement.of("A").isValidFor(ValidateableCodePoint.of(0, 0x61)), is(true)); } @Test void should_be_valid_if_codePoint_is_upercase_representation() { - assertThat(StringElement.of("A").isValidFor(0x41), is(true)); + assertThat(StringElement.of("A").isValidFor(ValidateableCodePoint.of(0, 0x41)), is(true)); } @Test void should_be_invalid_if_codePoint_is_neither_upercase_nor_lowercase_representation() { - assertThat(StringElement.of("A").isValidFor(0x42), is(false)); + assertThat(StringElement.of("A").isValidFor(ValidateableCodePoint.of(0, 0x42)), is(false)); } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java new file mode 100644 index 00000000..cb7d8b90 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java @@ -0,0 +1,59 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class ValidateableCodePointTest { + + @Test + void should_equals_contract() { + EqualsVerifier.forClass(ValidateableCodePoint.class).verify(); + } + + @Test + void should_not_be_creatable_with_position_lower_zero() { + assertThrows(IllegalArgumentException.class, () -> ValidateableCodePoint.of(-1, 0x00)); + } + + @Test + void should_not_be_creatable_with_codepoint_lower_zero() { + assertThrows(IllegalArgumentException.class, () -> ValidateableCodePoint.of(0, -1)); + } + + @Test + void should_return_his_position() { + assertThat(ValidateableCodePoint.of(10, 0x13).position(), is(10)); + } + + @Test + void should_repositionable_by_offset() { + assertThat(ValidateableCodePoint.of(10, 0x13).repositionBackBy(9), is(ValidateableCodePoint.of(1, 0x13))); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java index a0fe1cfc..1675aabb 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java @@ -61,7 +61,7 @@ void should_be_invalid_if_value_too_small() { NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) ) - .isValidFor(0x01), + .isValidFor(ValidateableCodePoint.of(0, 0x01)), is(false) ); } @@ -74,7 +74,7 @@ void should_be_invalid_if_value_too_big() { NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) ) - .isValidFor(0x05), + .isValidFor(ValidateableCodePoint.of(0, 0x05)), is(false) ); } @@ -87,7 +87,7 @@ void should_be_valid_if_value_is_in_range() { NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) ) - .isValidFor(0x03), + .isValidFor(ValidateableCodePoint.of(0, 0x03)), is(true) ); } @@ -100,7 +100,7 @@ void should_be_valid_if_value_is_minimum() { NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) ) - .isValidFor(0x02), + .isValidFor(ValidateableCodePoint.of(0, 0x02)), is(true) ); } @@ -113,7 +113,7 @@ void should_be_valid_if_value_is_maximum() { NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) ) - .isValidFor(0x04), + .isValidFor(ValidateableCodePoint.of(0, 0x04)), is(true) ); } @@ -137,4 +137,17 @@ void should_be_printable() { ) ); } + + @Test + void should_return_two_as_dimension() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10101), + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10111) + ) + .dimension(), + is(Dimension.of(1)) + ); + } } diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java index 859f0146..8745067a 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java @@ -117,4 +117,54 @@ void should_be_printable_with_between() { ) ); } + + @Test + void should_dimension_as_multimple_of() { + assertThat( + VariableRepetition + .ofBetween(Alternative.of(StringElement.of("ab"), StringElement.of("cde")), 2, 4) + .dimension(), + is(Dimension.of(4, 12)) + ); + } + + @Test + void should_be_valid_if_codepoint_matches_codepoint_at_posion_of_first_repeation() { + assertThat( + VariableRepetition + .ofAtLeast(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(0, 'a')), + is(true) + ); + } + + @Test + void should_be_valid_if_codepoint_matches_codepoint_at_posion_of_any_repeation() { + assertThat( + VariableRepetition + .ofAtLeast(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(4, 'a')), + is(true) + ); + } + + @Test + void should_be_invalid_if_codepoint_not_equals_at_posion_for_min_repeation() { + assertThat( + VariableRepetition + .ofAtLeast(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(1, 'b')), + is(false) + ); + } + + @Test + void should_be_invalod_if_codepoint_out_of_posion() { + assertThat( + VariableRepetition + .ofAtMost(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(3, 'a')), + is(false) + ); + } } From 588a51ac8c7611b2ee24a7c4f053846c17847cb8 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:56:00 +0100 Subject: [PATCH 09/11] consolidate duplicated code --- .../assertion/abnf/element/Concatenation.java | 11 +--- .../abnf/element/ListValidation.java | 53 +++++++++++++++++++ .../assertion/abnf/element/SequenceGroup.java | 8 +-- 3 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java index 01512afd..6ec49f07 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java @@ -57,21 +57,14 @@ public > T printOn(final T media) { @Override public Dimension dimension() { - return concatenations.stream().map(Element::dimension).reduce(Dimension::plus).orElseThrow(); + return concatenations.stream().map(Element::dimension).reduce(Dimension.zero(), Dimension::plus); } @Override public boolean isValidFor(final ValidateableCodePoint codePoint) { final boolean result; if (dimension().isInRange(codePoint)) { - final Element first = concatenations.get(0); - if (first.dimension().isInRange(codePoint)) { - result = first.isValidFor(codePoint); - } else { - result = - of(concatenations.subList(1, concatenations.size())) - .isValidFor(codePoint.repositionBackBy(first.dimension())); - } + result = new ListValidation(concatenations, Concatenation::of).isValidFor(codePoint); } else { result = false; } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java new file mode 100644 index 00000000..8aa671ac --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +class ListValidation { + + private final List elements; + private final Function, Element> newElement; + + public ListValidation(final List elements, final Function, Element> newElement) { + this.elements = List.copyOf(elements); + this.newElement = Objects.requireNonNull(newElement); + } + + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + final Element first = elements.get(0); + if (first.dimension().isInRange(codePoint)) { + result = first.isValidFor(codePoint); + } else { + result = + newElement + .apply(elements.subList(1, elements.size())) + .isValidFor(codePoint.repositionBackBy(first.dimension())); + } + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java index 4518b89a..b454c605 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java @@ -63,13 +63,7 @@ public > T printOn(final T media) { public boolean isValidFor(final ValidateableCodePoint codePoint) { final boolean result; if (dimension().isInRange(codePoint)) { - final Element first = elements.get(0); - if (first.dimension().isInRange(codePoint)) { - result = first.isValidFor(codePoint); - } else { - result = - of(elements.subList(1, elements.size())).isValidFor(codePoint.repositionBackBy(first.dimension())); - } + result = new ListValidation(elements, SequenceGroup::of).isValidFor(codePoint); } else { result = false; } From d55566f6fe954cf3551fae32693fde90db2f9fd9 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:56:25 +0100 Subject: [PATCH 10/11] add a java21 version of ListValidation --- .../abnf/element/ListValidation.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java diff --git a/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java b/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java new file mode 100644 index 00000000..f1c2aa30 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright 2023 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.format.assertion.abnf.element; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +class ListValidation { + + private final List elements; + private final Function, Element> newElement; + + public ListValidation(final List elements, final Function, Element> newElement) { + this.elements = List.copyOf(elements); + this.newElement = Objects.requireNonNull(newElement); + } + + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + final Element first = elements.getFirst(); + if (first.dimension().isInRange(codePoint)) { + result = first.isValidFor(codePoint); + } else { + result = + newElement + .apply(elements.subList(1, elements.size())) + .isValidFor(codePoint.repositionBackBy(first.dimension())); + } + return result; + } +} From 45d988b458b76f4e453c59610978c6857a211c5e Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:32:38 +0100 Subject: [PATCH 11/11] first poc to show the idea behind the rules --- .../assertion/FormatAssertionKeywordType.java | 16 +++++- .../format/assertion/abnf/Rule.java | 12 ++++ .../abnf/element/ListValidation.java | 53 ------------------ .../abnf/reader/ConcationTypeSwitch.java | 56 ------------------- .../FormatAssertionKeywordTypeTest.java | 16 ++---- .../format/assertion/abnf/RuleTest.java | 50 +++++++++++++++++ 6 files changed, 81 insertions(+), 122 deletions(-) delete mode 100644 vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java delete mode 100644 vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java index 435dd4e9..8ff11ffd 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java @@ -23,6 +23,7 @@ */ package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion; +import io.github.sebastiantoepfer.ddd.common.Media; import io.github.sebastiantoepfer.jsonschema.InstanceType; import io.github.sebastiantoepfer.jsonschema.JsonSchema; import io.github.sebastiantoepfer.jsonschema.keyword.Annotation; @@ -50,9 +51,13 @@ public String name() { } @Override - public Keyword createKeyword(final JsonSchema schema, final JsonValue value) { - if (InstanceType.STRING.isInstance(value)) { - return createKeyword(((JsonString) value).getString()); + public Keyword createKeyword(final JsonSchema js) { + if ( + js.getValueType() == JsonValue.ValueType.OBJECT && + js.asJsonObject().containsKey(name()) && + js.asJsonObject().get(name()).getValueType() == JsonValue.ValueType.STRING + ) { + return createKeyword(js.asJsonObject().getString(name())); } else { throw new IllegalArgumentException("Value must be a string!"); } @@ -94,5 +99,10 @@ public JsonValue valueFor(final JsonValue value) { public boolean isValidFor(final JsonValue instance) { return !InstanceType.STRING.isInstance(instance) || format.isValidFor(((JsonString) instance).getString()); } + + @Override + public > T printOn(final T media) { + return media.withValue(name(), format.name()); + } } } diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java index 4bb978ec..cb28b179 100644 --- a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java @@ -27,7 +27,10 @@ import io.github.sebastiantoepfer.ddd.common.Printable; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.IntStream; public final class Rule implements Printable { @@ -47,6 +50,15 @@ public boolean hasRuleName(final RuleName name) { return Objects.equals(this.name, name); } + Predicate asPredicate() { + return s -> + IntStream + .range(0, s.length()) + .boxed() + .map(i -> ValidateableCodePoint.of(i, s.codePointAt(i))) + .allMatch(elements::isValidFor); + } + @Override public > T printOn(final T media) { return media.withValue("name", name).withValue("type", "rule").withValue("elements", elements); diff --git a/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java b/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java deleted file mode 100644 index f1c2aa30..00000000 --- a/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * The MIT License - * - * Copyright 2023 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.format.assertion.abnf.element; - -import java.util.List; -import java.util.Objects; -import java.util.function.Function; - -class ListValidation { - - private final List elements; - private final Function, Element> newElement; - - public ListValidation(final List elements, final Function, Element> newElement) { - this.elements = List.copyOf(elements); - this.newElement = Objects.requireNonNull(newElement); - } - - public boolean isValidFor(final ValidateableCodePoint codePoint) { - final boolean result; - final Element first = elements.getFirst(); - if (first.dimension().isInRange(codePoint)) { - result = first.isValidFor(codePoint); - } else { - result = - newElement - .apply(elements.subList(1, elements.size())) - .isValidFor(codePoint.repositionBackBy(first.dimension())); - } - return result; - } -} diff --git a/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java b/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java deleted file mode 100644 index 5fc7a5db..00000000 --- a/vocabulary-format-assertion/src/main/java21/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * The MIT License - * - * Copyright 2023 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.format.assertion.abnf.reader; - -import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -class ConcationTypeSwitch { - - private final ExtractorOwner owner; - private final ElementEndDetector endOfElementDetector; - private final List elements; - - public ConcationTypeSwitch( - final ExtractorOwner owner, - final ElementEndDetector endOfElementDetector, - final List elements - ) { - this.owner = Objects.requireNonNull(owner); - this.endOfElementDetector = Objects.requireNonNull(endOfElementDetector); - this.elements = List.copyOf(elements); - } - - public Extractor switchTo(final ExtractorOwnerFactory ownerFactory, final ExtractorFactory targetFactory) { - final List newElements = new ArrayList<>(elements); - final Element element = newElements.removeLast(); - return targetFactory.create( - ownerFactory.create(owner, endOfElementDetector, newElements), - endOfElementDetector, - element - ); - } -} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java index 31e5de1b..f316b82c 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java @@ -42,7 +42,7 @@ class FormatAssertionKeywordTypeTest { @Test void should_know_his_name() { final Keyword keyword = new FormatAssertionKeywordType(List.of(new TestFormat())) - .createKeyword(JsonSchemas.load(JsonValue.TRUE), Json.createValue("date")); + .createKeyword(JsonSchemas.load(Json.createObjectBuilder().add("format", "date").build())); assertThat(keyword.hasName("format"), is(true)); assertThat(keyword.hasName("test"), is(false)); @@ -52,25 +52,21 @@ void should_know_his_name() { void should_not_be_createable_with_non_string() { final JsonSchema schema = JsonSchemas.load(JsonValue.TRUE); final KeywordType keywordType = new FormatAssertionKeywordType(List.of(new TestFormat())); - assertThrows( - IllegalArgumentException.class, - () -> keywordType.createKeyword(schema, JsonValue.EMPTY_JSON_OBJECT) - ); + assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(schema)); } @Test void should_not_be_createable_with_unknown_formatname() { - final JsonSchema schema = JsonSchemas.load(JsonValue.TRUE); + final JsonSchema schema = JsonSchemas.load(Json.createObjectBuilder().add("format", "test").build()); final KeywordType keywordType = new FormatAssertionKeywordType(List.of(new TestFormat())); - final JsonValue unknownFormatName = Json.createValue("test"); - assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(schema, unknownFormatName)); + assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(schema)); } @Test void should_be_assertion_and_annotation() { assertThat( new FormatAssertionKeywordType(List.of(new TestFormat())) - .createKeyword(JsonSchemas.load(JsonValue.TRUE), Json.createValue("date")) + .createKeyword(JsonSchemas.load(Json.createObjectBuilder().add("format", "date").build())) .categories(), containsInAnyOrder(Keyword.KeywordCategory.ASSERTION, Keyword.KeywordCategory.ANNOTATION) ); @@ -80,7 +76,7 @@ void should_be_assertion_and_annotation() { void should_return_formatname_as_annotation() { assertThat( new FormatAssertionKeywordType(List.of(new TestFormat())) - .createKeyword(JsonSchemas.load(JsonValue.TRUE), Json.createValue("date")) + .createKeyword(JsonSchemas.load(Json.createObjectBuilder().add("format", "date").build())) .asAnnotation() .valueFor(JsonValue.EMPTY_JSON_OBJECT), is(Json.createValue("date")) diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java index 124baa5c..9494d5c9 100644 --- a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java @@ -31,11 +31,16 @@ import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; import nl.jqno.equalsverifier.EqualsVerifier; import org.hamcrest.Matcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class RuleTest { @@ -78,4 +83,49 @@ void should_be_printable() { ) ); } + + @Nested + class Validation { + + private Rule ruleName; + + @BeforeEach + void initRule() { + ruleName = + Rule.of( + RuleName.of("rulename"), + Concatenation.of( + CoreRules.ALPHA, + VariableRepetition.of( + SequenceGroup.of(Alternative.of(CoreRules.ALPHA, CoreRules.DIGIT, StringElement.of("-"))) + ) + ) + ); + } + + @Test + void should_be_valid_for_alphas_only() { + assertThat(ruleName.asPredicate().test("rulename"), is(true)); + } + + @Test + void should_be_valid_for_valid_alpha_and_digits() { + assertThat(ruleName.asPredicate().test("rul3nam3"), is(true)); + } + + @Test + void should_be_valid_for_valid_alpha_and_minus() { + assertThat(ruleName.asPredicate().test("rule-name"), is(true)); + } + + @Test + void should_be_invalid_for_value_with_digist_at_start() { + assertThat(ruleName.asPredicate().test("1rule"), is(false)); + } + + @Test + void should_be_invalid_for_value_with_solidus() { + assertThat(ruleName.asPredicate().test("rule/name"), is(false)); + } + } }