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 extends Element> alternatives) {
+ return new Alternative(alternatives);
+ }
+
+ private final List alternatives;
+
+ private Alternative(final Collection extends Element> 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 extends Element> alternatives) {
+ return new Concatenation(alternatives);
+ }
+
+ private final List concatenations;
+
+ private Concatenation(final Collection extends Element> 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 extends Element> elements) {
+ return new SequenceGroup(elements);
+ }
+
+ private final List elements;
+
+ private SequenceGroup(final Collection extends Element> 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 extends Element> 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 extends Element> 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 extends Element> 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));
+ }
+ }
}