From 130277a4aae6a519d84b1ab0dba207b738f91183 Mon Sep 17 00:00:00 2001
From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com>
Date: Thu, 2 Nov 2023 00:38:01 +0100
Subject: [PATCH] add abnf basic structure
---
vocabulary-format-assertion/pom.xml | 13 +-
.../format/assertion/abnf/CoreRules.java | 165 +++++++++++++++
.../format/assertion/abnf/Rule.java | 78 +++++++
.../format/assertion/abnf/RuleList.java | 77 +++++++
.../assertion/abnf/element/Alternative.java | 82 ++++++++
.../abnf/element/ConcatedString.java | 87 ++++++++
.../assertion/abnf/element/Concatenation.java | 82 ++++++++
.../assertion/abnf/element/Element.java} | 10 +-
.../abnf/element/NumericCharacter.java | 109 ++++++++++
.../abnf/element/OptionalSequence.java | 71 +++++++
.../assertion/abnf/element/RuleName.java | 67 ++++++
.../assertion/abnf/element/RuleReference.java | 71 +++++++
.../assertion/abnf/element/SequenceGroup.java | 71 +++++++
.../abnf/element/SpecificRepetition.java | 77 +++++++
.../assertion/abnf/element/StringElement.java | 76 +++++++
.../abnf/element/ValueRangeAlternatives.java | 77 +++++++
.../abnf/element/VariableRepetition.java | 108 ++++++++++
.../format/assertion/abnf/reader/ABNF.java | 30 +++
.../abnf/reader/AlternativeDecisionMaker.java | 72 +++++++
.../abnf/reader/AlternativeExtractor.java | 99 +++++++++
.../abnf/reader/CommentaryExtractor.java | 63 ++++++
.../assertion/abnf/reader/ElementToken.java | 30 +++
.../abnf/reader/ElementsExtractor.java | 135 ++++++++++++
.../assertion/abnf/reader/Extractor.java | 30 +++
.../abnf/reader/ExtractorEndsWithNewLine.java | 74 +++++++
.../ExtractorWithEndpositionDetection.java | 31 +++
.../abnf/reader/NewLineDetector.java | 77 +++++++
.../reader/NumericCharactersExtractor.java | 82 ++++++++
.../reader/OptionalSequenceExtractor.java | 61 ++++++
.../abnf/reader/RepetitionExtractor.java | 149 +++++++++++++
.../assertion/abnf/reader/RuleExtractor.java | 128 +++++++++++
.../abnf/reader/RuleNameExtractor.java | 69 ++++++
.../abnf/reader/RuleReferenceExtractor.java | 59 ++++++
.../reader/SequenceGroupElementExtractor.java | 79 +++++++
.../abnf/reader/StringElementExtractor.java | 59 ++++++
.../assertion/abnf/reader/TextABNF.java | 79 +++++++
.../abnf/reader/UsefulCodepoints.java | 38 ++++
.../format/assertion/abnf/RuleTest.java | 48 +++++
.../abnf/element/AlternativeTest.java | 50 +++++
.../abnf/element/ConcatedStringTest.java | 35 +++
.../abnf/element/ConcatenationTest.java | 50 +++++
.../abnf/element/NumericCharacterTest.java | 35 +++
.../abnf/element/OptionalSequenceTest.java | 35 +++
.../assertion/abnf/element/RuleNameTest.java | 35 +++
.../abnf/element/SequenceGroupTest.java | 35 +++
.../abnf/element/SpecificRepetitionTest.java | 35 +++
.../abnf/element/StringElementTest.java | 35 +++
.../element/ValueRangeAlternativesTest.java | 51 +++++
.../abnf/element/VariableRepetitionTest.java | 46 ++++
.../abnf/reader/ElementsExtractorTest.java | 199 ++++++++++++++++++
.../abnf/reader/ExtractableTestString.java | 56 +++++
.../abnf/reader/RuleExtractorTest.java | 111 ++++++++++
.../assertion/abnf/reader/TextABNFTest.java | 103 +++++++++
53 files changed, 3710 insertions(+), 14 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/ConcatedString.java
create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java
rename vocabulary-format-assertion/src/main/java/{module-info.java => io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java} (73%)
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/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/AlternativeDecisionMaker.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/CommentaryExtractor.java
create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementToken.java
create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractor.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/ExtractorEndsWithNewLine.java
create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorWithEndpositionDetection.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/NumericCharactersExtractor.java
create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionalSequenceExtractor.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/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/SequenceGroupElementExtractor.java
create mode 100644 vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StringElementExtractor.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/UsefulCodepoints.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/ConcatedStringTest.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/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/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractorTest.java
create mode 100644 vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractableTestString.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/TextABNFTest.java
diff --git a/vocabulary-format-assertion/pom.xml b/vocabulary-format-assertion/pom.xml
index 9b8e321f..5c9aed12 100644
--- a/vocabulary-format-assertion/pom.xml
+++ b/vocabulary-format-assertion/pom.xml
@@ -25,13 +25,11 @@
org.junit.jupiter
junit-jupiter-api
test
- 5.6.0
org.junit.jupiter
junit-jupiter-engine
test
- 5.6.0
org.hamcrest
@@ -43,6 +41,11 @@
hamcrest-optional
test
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ test
+
${project.groupId}
json-schema-core
@@ -61,11 +64,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..eebf95b3
--- /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, 0x51A)
+ ),
+ 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..fdb83bf0
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java
@@ -0,0 +1,82 @@
+/*
+ * 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.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 List extends Element> alternatives) {
+ return new Alternative(alternatives);
+ }
+
+ private final List alternatives;
+
+ private Alternative(final List 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/ConcatedString.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatedString.java
new file mode 100644
index 00000000..10c01bc9
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatedString.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 ConcatedString implements Element {
+
+ public static ConcatedString of(
+ final NumericCharacter first,
+ final NumericCharacter second,
+ final NumericCharacter... more
+ ) {
+ final List values = new ArrayList<>();
+ values.add(first);
+ values.add(second);
+ values.addAll(Arrays.asList(more));
+ return new ConcatedString(values);
+ }
+
+ public static ConcatedString of(final Collection values) {
+ return new ConcatedString(values);
+ }
+
+ private final Collection values;
+
+ private ConcatedString(final Collection values) {
+ this.values = List.copyOf(values);
+ }
+
+ @Override
+ public boolean isValidFor(final int codePoint) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 23 * hash + Objects.hashCode(this.values);
+ 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 ConcatedString other = (ConcatedString) obj;
+ return Objects.equals(this.values, other.values);
+ }
+
+ @Override
+ public String toString() {
+ return "ConcatedString{" + "values=" + values + '}';
+ }
+}
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..543a9b02
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java
@@ -0,0 +1,82 @@
+/*
+ * 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.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 List extends Element> alternatives) {
+ return new Concatenation(alternatives);
+ }
+
+ private final List concatenations;
+
+ private Concatenation(final List 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/module-info.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java
similarity index 73%
rename from vocabulary-format-assertion/src/main/java/module-info.java
rename to vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java
index 66c685cd..ad3999c7 100644
--- a/vocabulary-format-assertion/src/main/java/module-info.java
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java
@@ -21,11 +21,9 @@
* 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;
+package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element;
- provides io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies
- with io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.FormatAssertionVocabulary;
+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..fa495fc7
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java
@@ -0,0 +1,109 @@
+/*
+ * 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(() -> new IllegalArgumentException(String.format("\"%c\" is not a valid base!", baseChar))
+ );
+ }
+ }
+}
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..7579338d
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.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 OptionalSequence implements Element {
+
+ public static OptionalSequence of(final Element optionalElement) {
+ return new OptionalSequence(optionalElement);
+ }
+
+ private final Element optionalElement;
+
+ public OptionalSequence(final Element optionalElement) {
+ this.optionalElement = Objects.requireNonNull(optionalElement);
+ }
+
+ @Override
+ public boolean isValidFor(final int codePoint) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public String toString() {
+ return "OptionalSequence{" + "optionalElement=" + optionalElement + '}';
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 67 * hash + Objects.hashCode(this.optionalElement);
+ 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.optionalElement, other.optionalElement);
+ }
+}
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..5521649b
--- /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 = name.toLowerCase(Locale.US);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 67 * 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 RuleName other = (RuleName) obj;
+ return Objects.equals(this.name, other.name);
+ }
+
+ @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..b201689a
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.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 SequenceGroup implements Element {
+
+ public static SequenceGroup of(final Element term) {
+ return new SequenceGroup(term);
+ }
+
+ private final Element term;
+
+ private SequenceGroup(final Element term) {
+ this.term = Objects.requireNonNull(term);
+ }
+
+ @Override
+ public boolean isValidFor(final int codePoint) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 83 * hash + Objects.hashCode(this.term);
+ 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.term, other.term);
+ }
+
+ @Override
+ public String toString() {
+ return "SequenceGroup{" + "term=" + term + '}';
+ }
+}
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..d18e76af
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java
@@ -0,0 +1,76 @@
+/*
+ * 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 = value.toLowerCase(Locale.US);
+ }
+
+ @Override
+ public boolean isValidFor(final int codePoint) {
+ if (value.length() == 1) {
+ final int lowerCaseCodePoint = value.codePointAt(0);
+ return lowerCaseCodePoint == codePoint || Character.toUpperCase(lowerCaseCodePoint) == codePoint;
+ }
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 17 * hash + Objects.hashCode(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 StringElement other = (StringElement) obj;
+ return Objects.equals(this.value, other.value);
+ }
+
+ @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/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/AlternativeDecisionMaker.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeDecisionMaker.java
new file mode 100644
index 00000000..186a3492
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeDecisionMaker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.Element;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+class AlternativeDecisionMaker implements ExtractorWithEndpositionDetection {
+
+ public static ExtractorWithEndpositionDetection of(
+ final ExtractorWithEndpositionDetection posibleAlternative,
+ final Supplier> newElementExtractor
+ ) {
+ return new AlternativeDecisionMaker(posibleAlternative, newElementExtractor);
+ }
+
+ private final ExtractorWithEndpositionDetection posibleAlternative;
+ private final Supplier> newElementExtractor;
+
+ private AlternativeDecisionMaker(
+ final ExtractorWithEndpositionDetection posibleAlternative,
+ final Supplier> newElementExtractor
+ ) {
+ this.posibleAlternative = Objects.requireNonNull(posibleAlternative);
+ this.newElementExtractor = Objects.requireNonNull(newElementExtractor);
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return codePoint != SOLIDUS && posibleAlternative.hasNoInteresedIn(codePoint);
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ final ExtractorWithEndpositionDetection result;
+ if (codePoint == SOLIDUS) {
+ result = AlternativeExtractor.of(posibleAlternative, newElementExtractor);
+ } else {
+ result = new AlternativeDecisionMaker(posibleAlternative.append(codePoint), newElementExtractor);
+ }
+ return result;
+ }
+
+ @Override
+ public Element create() {
+ return posibleAlternative.create();
+ }
+}
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..d7343689
--- /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.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+final class AlternativeExtractor implements ExtractorWithEndpositionDetection {
+
+ public static ExtractorWithEndpositionDetection of(
+ final Extractor first,
+ final Supplier> newElementExtractor
+ ) {
+ return new AlternativeExtractor(first, newElementExtractor);
+ }
+
+ private final List> left;
+ private final Supplier> newElementExtractor;
+ private final ExtractorWithEndpositionDetection currentElement;
+
+ public AlternativeExtractor(
+ final Extractor left,
+ final Supplier> newElementExtractor
+ ) {
+ this(List.of(left), newElementExtractor);
+ }
+
+ public AlternativeExtractor(
+ final List> left,
+ final Supplier> newElementExtractor
+ ) {
+ this(left, newElementExtractor, newElementExtractor.get());
+ }
+
+ public AlternativeExtractor(
+ final List> left,
+ final Supplier> newElementExtractor,
+ final ExtractorWithEndpositionDetection currentElement
+ ) {
+ this.left = Objects.requireNonNull(left);
+ this.newElementExtractor = Objects.requireNonNull(newElementExtractor);
+ this.currentElement = Objects.requireNonNull(currentElement);
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return codePoint != SOLIDUS && currentElement.hasNoInteresedIn(codePoint);
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ final AlternativeExtractor result;
+ if (codePoint == SOLIDUS) {
+ final ArrayList> newLeft = new ArrayList<>(left);
+ newLeft.add(currentElement);
+ result = new AlternativeExtractor(newLeft, newElementExtractor, newElementExtractor.get());
+ } else {
+ result = new AlternativeExtractor(left, newElementExtractor, currentElement.append(codePoint));
+ }
+ return result;
+ }
+
+ @Override
+ public Element create() {
+ return Stream
+ .concat(left.stream(), Stream.of(currentElement))
+ .map(Extractor::create)
+ .collect(Collectors.collectingAndThen(Collectors.toList(), Alternative::of));
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CommentaryExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CommentaryExtractor.java
new file mode 100644
index 00000000..e38540be
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CommentaryExtractor.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 java.util.logging.Logger;
+
+final class CommentaryExtractor implements Extractor {
+
+ private static final Logger LOG = Logger.getLogger(CommentaryExtractor.class.getName());
+
+ static CommentaryExtractor startCommentary() {
+ return new CommentaryExtractor();
+ }
+
+ private final NewLineDetector newLine;
+
+ public CommentaryExtractor() {
+ this(new NewLineDetector());
+ }
+
+ public CommentaryExtractor(final NewLineDetector newLine) {
+ this.newLine = newLine;
+ }
+
+ @Override
+ public CommentaryExtractor append(final int codePoint) {
+ LOG.entering(CommentaryExtractor.class.getName(), "append", codePoint);
+ final CommentaryExtractor result = new CommentaryExtractor(newLine.append(codePoint));
+ LOG.exiting(CommentaryExtractor.class.getName(), "append", result);
+ return result;
+ }
+
+ @Override
+ public String create() {
+ return "";
+ }
+
+ @Override
+ public String toString() {
+ return "CommentaryExtractor{" + "newLine=" + newLine + '}';
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementToken.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementToken.java
new file mode 100644
index 00000000..72e0c369
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementToken.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.element.Element;
+
+interface ElementToken {
+ Element createElement();
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractor.java
new file mode 100644
index 00000000..ed5b0c8d
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractor.java
@@ -0,0 +1,135 @@
+/*
+ * 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 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.QUOTATION_MARK;
+
+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.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+final class ElementsExtractor implements ExtractorWithEndpositionDetection {
+
+ static ElementsExtractor startElements() {
+ return new ElementsExtractor();
+ }
+
+ private final List> elements;
+ private ExtractorWithEndpositionDetection currentElement;
+
+ public ElementsExtractor() {
+ this(List.of(), null);
+ }
+
+ private ElementsExtractor(
+ final List> elements,
+ final ExtractorWithEndpositionDetection currentElement
+ ) {
+ this.elements = elements;
+ this.currentElement = currentElement;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return currentElement != null && currentElement.hasNoInteresedIn(codePoint);
+ }
+
+ @Override
+ public Element create() {
+ final Element result;
+ if (currentElement == null && elements.size() == 1) {
+ result = elements.iterator().next().create();
+ } else if (currentElement == null) {
+ result =
+ elements
+ .stream()
+ .map(Extractor::create)
+ .collect(Collectors.collectingAndThen(Collectors.toList(), Concatenation::of));
+ } else if (elements.isEmpty()) {
+ result = currentElement.create();
+ } else {
+ result =
+ Stream
+ .concat(elements.stream(), Stream.of(currentElement))
+ .map(Extractor::create)
+ .collect(Collectors.collectingAndThen(Collectors.toList(), Concatenation::of));
+ }
+ return result;
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ final ExtractorWithEndpositionDetection result;
+ if (currentElement == null && Character.isWhitespace(codePoint)) {
+ result = this;
+ } else if (currentElement == null) {
+ if (Character.isDigit(codePoint) || codePoint == ASTERISK) {
+ result =
+ new ElementsExtractor(
+ elements,
+ RepetitionExtractor.of(c -> ElementsExtractor.startElements().append(c), codePoint)
+ );
+ } else if (codePoint == NumericCharactersExtractor.PERCENT_SIGN) {
+ result =
+ AlternativeDecisionMaker.of(
+ new ElementsExtractor(elements, NumericCharactersExtractor.of()),
+ ElementsExtractor::startElements
+ );
+ } else if (codePoint == LEFT_PARENTHESIS) {
+ result = new ElementsExtractor(elements, SequenceGroupElementExtractor.startSequenceGroup());
+ } else if (codePoint == LEFT_SQUARE_BRACKET) {
+ result =
+ new ElementsExtractor(elements, new OptionalSequenceExtractor(ElementsExtractor::startElements));
+ } else if (codePoint == QUOTATION_MARK) {
+ result =
+ AlternativeDecisionMaker.of(
+ new ElementsExtractor(elements, StringElementExtractor.startNewSting()),
+ ElementsExtractor::startElements
+ );
+ } else if (Character.isAlphabetic(codePoint)) {
+ result =
+ AlternativeDecisionMaker.of(
+ new ElementsExtractor(elements, RuleReferenceExtractor.of(codePoint)),
+ ElementsExtractor::startElements
+ );
+ } else {
+ result = this;
+ }
+ } else if (currentElement.hasNoInteresedIn(codePoint)) {
+ final List> newElements = new ArrayList<>();
+ newElements.addAll(elements);
+ newElements.add(currentElement);
+ result = new ElementsExtractor(newElements, null).append(codePoint);
+ } else {
+ result = new ElementsExtractor(elements, currentElement.append(codePoint));
+ }
+ 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..dd3fb40d
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.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;
+
+interface Extractor {
+ Extractor append(int codePoint);
+
+ T create();
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorEndsWithNewLine.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorEndsWithNewLine.java
new file mode 100644
index 00000000..90878c5c
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorEndsWithNewLine.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 java.util.logging.Logger;
+
+final class ExtractorEndsWithNewLine implements ExtractorWithEndpositionDetection {
+
+ private static final Logger LOG = Logger.getLogger(ExtractorEndsWithNewLine.class.getName());
+
+ public static ExtractorEndsWithNewLine of(final Extractor extractor) {
+ return new ExtractorEndsWithNewLine<>(extractor);
+ }
+
+ private final Extractor extractor;
+ private final NewLineDetector newLine;
+
+ private ExtractorEndsWithNewLine(final Extractor extractor) {
+ this(extractor, new NewLineDetector());
+ }
+
+ private ExtractorEndsWithNewLine(final Extractor extractor, final NewLineDetector newLine) {
+ this.extractor = extractor;
+ this.newLine = newLine;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ LOG.entering(ExtractorEndsWithNewLine.class.getName(), "hasNoInteresedIn", codePoint);
+ final boolean result = newLine.hasNoInteresedIn(codePoint);
+ LOG.exiting(ExtractorEndsWithNewLine.class.getName(), "hasNoInteresedIn", result);
+ return result;
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ LOG.entering(ExtractorEndsWithNewLine.class.getName(), "append", codePoint);
+ final ExtractorEndsWithNewLine result = new ExtractorEndsWithNewLine<>(
+ extractor.append(codePoint),
+ newLine.append(codePoint)
+ );
+ LOG.exiting(ExtractorEndsWithNewLine.class.getName(), "append", result);
+ return result;
+ }
+
+ @Override
+ public T create() {
+ LOG.entering(ExtractorEndsWithNewLine.class.getName(), "create");
+ final T result = extractor.create();
+ LOG.exiting(ExtractorEndsWithNewLine.class.getName(), "create", result);
+ return result;
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorWithEndpositionDetection.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorWithEndpositionDetection.java
new file mode 100644
index 00000000..7776454c
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorWithEndpositionDetection.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;
+
+interface ExtractorWithEndpositionDetection extends Extractor {
+ boolean hasNoInteresedIn(int codePoint);
+
+ @Override
+ ExtractorWithEndpositionDetection append(int codePoint);
+}
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..5ea48878
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.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.reader;
+
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules;
+import java.util.logging.Logger;
+
+final class NewLineDetector implements ExtractorWithEndpositionDetection {
+
+ private static final Logger LOG = Logger.getLogger(NewLineDetector.class.getName());
+
+ private final boolean cr;
+ private final boolean lf;
+
+ public NewLineDetector() {
+ this(false, false);
+ }
+
+ private NewLineDetector(final boolean cr, final boolean lf) {
+ this.cr = cr;
+ this.lf = lf;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ LOG.entering(NewLineDetector.class.getName(), "hasNoInteresedIn", codePoint);
+ final boolean result = !CoreRules.WSP.isValidFor(codePoint) && cr && lf;
+ LOG.exiting(NewLineDetector.class.getName(), "hasNoInteresedIn", result);
+ return result;
+ }
+
+ @Override
+ public NewLineDetector append(final int codePoint) {
+ LOG.entering(NewLineDetector.class.getName(), "append", codePoint);
+ final NewLineDetector result;
+ if (cr && CoreRules.LF.isValidFor(codePoint)) {
+ result = new NewLineDetector(cr, true);
+ } else if (cr) {
+ result = new NewLineDetector();
+ } else {
+ result = new NewLineDetector(CoreRules.CR.isValidFor(codePoint), false);
+ }
+ LOG.exiting(NewLineDetector.class.getName(), "append", result);
+ return result;
+ }
+
+ @Override
+ public String create() {
+ return "";
+ }
+
+ @Override
+ public String toString() {
+ return "NewLineDetector{" + "cr=" + cr + ", lf=" + lf + '}';
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumericCharactersExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumericCharactersExtractor.java
new file mode 100644
index 00000000..037cf1d0
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumericCharactersExtractor.java
@@ -0,0 +1,82 @@
+/*
+ * 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 io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter;
+import java.util.logging.Logger;
+
+final class NumericCharactersExtractor implements ExtractorWithEndpositionDetection {
+
+ private static final Logger LOG = Logger.getLogger(NumericCharactersExtractor.class.getName());
+ static final int PERCENT_SIGN = 0x25;
+
+ static NumericCharactersExtractor of() {
+ return new NumericCharactersExtractor();
+ }
+
+ private final NumericCharacter.BASE base;
+ private final StringBuilder value;
+
+ private NumericCharactersExtractor() {
+ this(null, new StringBuilder());
+ }
+
+ public NumericCharactersExtractor(final NumericCharacter.BASE base, final StringBuilder value) {
+ this.base = base;
+ this.value = value;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return Character.isWhitespace(codePoint);
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ LOG.entering(NumericCharactersExtractor.class.getName(), "append", codePoint);
+ final ExtractorWithEndpositionDetection result;
+ if (base == null && Character.isAlphabetic(codePoint)) {
+ result =
+ new NumericCharactersExtractor(
+ NumericCharacter.BASE.findByShortName(Character.toChars(codePoint)[0]),
+ value
+ );
+ } else if (base == null) {
+ throw new IllegalStateException();
+ } else {
+ result = new NumericCharactersExtractor(base, value.appendCodePoint(codePoint));
+ }
+ LOG.entering(NumericCharactersExtractor.class.getName(), "append", result);
+ return result;
+ }
+
+ @Override
+ public Element create() {
+ LOG.entering(NumericCharactersExtractor.class.getName(), "create");
+ final NumericCharacter result = NumericCharacter.of(base, base.convert(value.toString()));
+ LOG.entering(NumericCharactersExtractor.class.getName(), "create");
+ return result;
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionalSequenceExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionalSequenceExtractor.java
new file mode 100644
index 00000000..ff483b71
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionalSequenceExtractor.java
@@ -0,0 +1,61 @@
+/*
+ * 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 io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence;
+import java.util.function.Supplier;
+
+final class OptionalSequenceExtractor implements ExtractorWithEndpositionDetection {
+
+ private final Supplier> newElementExtractor;
+ private final ExtractorWithEndpositionDetection currentElement;
+
+ public OptionalSequenceExtractor(final Supplier> newElementExtractor) {
+ this(newElementExtractor, newElementExtractor.get());
+ }
+
+ private OptionalSequenceExtractor(
+ final Supplier> newElementExtractor,
+ final ExtractorWithEndpositionDetection currentElement
+ ) {
+ this.newElementExtractor = newElementExtractor;
+ this.currentElement = currentElement;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return codePoint == UsefulCodepoints.RIGHT_SQUARE_BRACKET || currentElement.hasNoInteresedIn(codePoint);
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ return new OptionalSequenceExtractor(newElementExtractor, currentElement.append(codePoint));
+ }
+
+ @Override
+ public Element create() {
+ return OptionalSequence.of(currentElement.create());
+ }
+}
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..fb65d1da
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+import java.util.function.IntFunction;
+import java.util.logging.Logger;
+
+final class RepetitionExtractor implements ExtractorWithEndpositionDetection {
+
+ private static final Logger LOG = Logger.getLogger(RepetitionExtractor.class.getName());
+
+ static RepetitionExtractor of(
+ final IntFunction> extractorGenerator,
+ final int codePoint
+ ) {
+ final RepetitionExtractor result;
+ if (Character.isDigit(codePoint)) {
+ result = new RepetitionExtractor(extractorGenerator, Integer.valueOf(Character.toString(codePoint)));
+ } else {
+ result = new RepetitionExtractor(extractorGenerator);
+ }
+ return result;
+ }
+
+ private final IntFunction> extractorGenerator;
+ private final Integer atLeast;
+ private final boolean variable;
+ private final Integer atMost;
+ private ExtractorWithEndpositionDetection element;
+
+ private RepetitionExtractor(final IntFunction> extractorGenerator) {
+ this(extractorGenerator, null, true);
+ }
+
+ private RepetitionExtractor(
+ final IntFunction> extractorGenerator,
+ final Integer atLeast
+ ) {
+ this(extractorGenerator, atLeast, false);
+ }
+
+ private RepetitionExtractor(
+ final IntFunction> extractorGenerator,
+ final Integer atLeast,
+ final boolean variable
+ ) {
+ this(extractorGenerator, atLeast, variable, null, null);
+ }
+
+ private RepetitionExtractor(
+ final IntFunction> extractorGenerator,
+ final Integer atLeast,
+ final boolean variable,
+ final Integer atMost,
+ final ExtractorWithEndpositionDetection elements
+ ) {
+ this.extractorGenerator = Objects.requireNonNull(extractorGenerator);
+ this.atLeast = atLeast;
+ this.variable = variable;
+ this.atMost = atMost;
+ this.element = elements;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return element != null && element.hasNoInteresedIn(codePoint);
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ LOG.entering(RepetitionExtractor.class.getName(), "append", codePoint);
+ final ExtractorWithEndpositionDetection result;
+ if (element == null && Character.isWhitespace(codePoint)) {
+ result = this;
+ } else if (element == null) {
+ if (codePoint == ASTERISK) {
+ result = new RepetitionExtractor(extractorGenerator, atLeast, true, atMost, element);
+ } else if (Character.isDigit(codePoint)) {
+ result =
+ new RepetitionExtractor(
+ extractorGenerator,
+ atLeast,
+ variable,
+ Integer.valueOf(Character.toString(codePoint)),
+ element
+ );
+ } else {
+ result =
+ new RepetitionExtractor(
+ extractorGenerator,
+ atLeast,
+ variable,
+ atMost,
+ extractorGenerator.apply(codePoint)
+ );
+ }
+ } else {
+ result = new RepetitionExtractor(extractorGenerator, atLeast, variable, atMost, element.append(codePoint));
+ }
+ LOG.exiting(RepetitionExtractor.class.getName(), "append", result);
+ return result;
+ }
+
+ @Override
+ public Element create() {
+ LOG.entering(RepetitionExtractor.class.getName(), "create");
+ final Element result;
+ if (variable && atLeast == null && atMost == null) {
+ result = VariableRepetition.of(element.create());
+ } else if (variable && atLeast == null) {
+ result = VariableRepetition.ofAtMost(element.create(), atMost);
+ } else if (variable && atMost == null) {
+ result = VariableRepetition.ofAtLeast(element.create(), atLeast);
+ } else if (variable) {
+ result = VariableRepetition.ofBetween(element.create(), atLeast, atMost);
+ } else {
+ result = SpecificRepetition.of(element.create(), atLeast);
+ }
+ LOG.exiting(RepetitionExtractor.class.getName(), "create", result);
+ return result;
+ }
+}
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..1a8fe9ce
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java
@@ -0,0 +1,128 @@
+/*
+ * 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.element.Element;
+import java.util.logging.Logger;
+
+final class RuleExtractor implements ExtractorWithEndpositionDetection {
+
+ private static final Logger LOG = Logger.getLogger(RuleExtractor.class.getName());
+
+ static RuleExtractor startNewRule(final int codePoint) {
+ return new RuleExtractor(codePoint);
+ }
+
+ private final RuleNameExtractor name;
+ private final ExtractorWithEndpositionDetection elements;
+ private final ExtractorWithEndpositionDetection commentary;
+
+ public RuleExtractor(final int codePoint) {
+ this(RuleNameExtractor.of(codePoint), null, null);
+ }
+
+ private RuleExtractor(
+ final RuleNameExtractor name,
+ final ExtractorWithEndpositionDetection elements,
+ final ExtractorWithEndpositionDetection commentary
+ ) {
+ this.name = name;
+ this.elements = elements;
+ this.commentary = commentary;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ LOG.entering(RuleExtractor.class.getName(), "hasNoInteresedIn", codePoint);
+ final boolean result =
+ (commentary != null && commentary.hasNoInteresedIn(codePoint)) ||
+ (elements != null && elements.hasNoInteresedIn(codePoint));
+ LOG.exiting(RuleExtractor.class.getName(), "hasNoInteresedIn", result);
+ return result;
+ }
+
+ @Override
+ public Rule create() {
+ LOG.entering(RuleExtractor.class.getName(), "createRule");
+ final Rule result;
+ if (elements == null) {
+ throw new IllegalStateException("no elements");
+ } else {
+ result = Rule.of(name.create(), elements.create());
+ }
+ LOG.exiting(RuleExtractor.class.getName(), "createRule", result);
+ return result;
+ }
+
+ @Override
+ public RuleExtractor append(final int codePoint) {
+ LOG.entering(RuleExtractor.class.getName(), "append");
+ final RuleExtractor result;
+ if (commentary == null) {
+ result = processAsNonCommentary(codePoint);
+ } else {
+ result = processAsCommentray(codePoint);
+ }
+ LOG.exiting(RuleExtractor.class.getName(), "append", result);
+ return result;
+ }
+
+ private RuleExtractor processAsNonCommentary(final int codePoint) {
+ LOG.entering(RuleExtractor.class.getName(), "processAsNonComentary");
+ RuleExtractor result;
+ if (codePoint == 0x3B) {
+ result =
+ new RuleExtractor(name, elements, ExtractorEndsWithNewLine.of(CommentaryExtractor.startCommentary()));
+ } else if (elements == null) {
+ result = processAsRuleName(codePoint);
+ } else {
+ result = new RuleExtractor(name, elements.append(codePoint), commentary);
+ }
+ LOG.exiting(RuleExtractor.class.getName(), "processAsNonComentary");
+ return result;
+ }
+
+ private RuleExtractor processAsRuleName(final int codePoint) {
+ LOG.entering(RuleExtractor.class.getName(), "processAsRuleName");
+ RuleExtractor result;
+ if (codePoint == 0x3D) {
+ result =
+ new RuleExtractor(name, ExtractorEndsWithNewLine.of(ElementsExtractor.startElements()), commentary);
+ } else if (Character.isWhitespace(codePoint)) {
+ result = this;
+ } else {
+ result = new RuleExtractor(name.append(codePoint), elements, commentary);
+ }
+ LOG.exiting(RuleExtractor.class.getName(), "processAsRuleName", result);
+ return result;
+ }
+
+ private RuleExtractor processAsCommentray(final int codePoint) {
+ LOG.entering(RuleExtractor.class.getName(), "processAsCommentray", codePoint);
+ final RuleExtractor result = new RuleExtractor(name, elements, commentary.append(codePoint));
+ LOG.exiting(RuleExtractor.class.getName(), "processAsCommentray", result);
+ return result;
+ }
+}
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..0573caa6
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.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;
+
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName;
+import java.util.logging.Logger;
+
+final class RuleNameExtractor implements Extractor {
+
+ private static final Logger LOG = Logger.getLogger(RuleNameExtractor.class.getName());
+
+ public static RuleNameExtractor of(final int codePoint) {
+ return new RuleNameExtractor(codePoint);
+ }
+
+ private final StringBuilder name;
+
+ private RuleNameExtractor(final int codePoint) {
+ this(new StringBuilder().appendCodePoint(codePoint));
+ }
+
+ private RuleNameExtractor(final StringBuilder name) {
+ this.name = name;
+ }
+
+ @Override
+ public RuleNameExtractor append(final int codePoint) {
+ LOG.entering(RuleNameExtractor.class.getName(), "append", codePoint);
+ final RuleNameExtractor result;
+ if (Character.isWhitespace(codePoint)) {
+ result = this;
+ } else {
+ result = new RuleNameExtractor(name.appendCodePoint(codePoint));
+ }
+ LOG.exiting(RuleNameExtractor.class.getName(), "append", result);
+ return result;
+ }
+
+ @Override
+ public RuleName create() {
+ return RuleName.of(name.toString());
+ }
+
+ @Override
+ public String toString() {
+ return "RuleNameTokens{" + "name=" + 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..2aa14f51
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.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.reader;
+
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element;
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference;
+
+final class RuleReferenceExtractor implements ExtractorWithEndpositionDetection {
+
+ public static ExtractorWithEndpositionDetection of(final int codePoint) {
+ return new RuleReferenceExtractor(codePoint);
+ }
+
+ private final RuleNameExtractor name;
+
+ private RuleReferenceExtractor(final int codePoint) {
+ this(RuleNameExtractor.of(codePoint));
+ }
+
+ private RuleReferenceExtractor(final RuleNameExtractor name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return codePoint == UsefulCodepoints.EQUALS_SIGN || Character.isWhitespace(codePoint);
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ return new RuleReferenceExtractor(name.append(codePoint));
+ }
+
+ @Override
+ public Element create() {
+ return RuleReference.of(name.create());
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/SequenceGroupElementExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/SequenceGroupElementExtractor.java
new file mode 100644
index 00000000..ab476c80
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/SequenceGroupElementExtractor.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+final class SequenceGroupElementExtractor implements ExtractorWithEndpositionDetection {
+
+ public static SequenceGroupElementExtractor startSequenceGroup() {
+ return new SequenceGroupElementExtractor();
+ }
+
+ private final Extractor element;
+ private final boolean rightParenthesis;
+
+ private SequenceGroupElementExtractor() {
+ this(null, false);
+ }
+
+ private SequenceGroupElementExtractor(final Extractor element, final boolean rightParenthesis) {
+ this.element = element;
+ this.rightParenthesis = rightParenthesis;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return rightParenthesis;
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ final ExtractorWithEndpositionDetection result;
+ if (element == null && Character.isWhitespace(codePoint)) {
+ result = this;
+ } else if (element == null) {
+ result =
+ new SequenceGroupElementExtractor(
+ ElementsExtractor.startElements().append(codePoint),
+ rightParenthesis
+ );
+ } else {
+ if (codePoint == RIGHT_PARENTHESIS) {
+ result = new SequenceGroupElementExtractor(element, true);
+ } else {
+ result = new SequenceGroupElementExtractor(element.append(codePoint), rightParenthesis);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Element create() {
+ return SequenceGroup.of(element.create());
+ }
+}
diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StringElementExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StringElementExtractor.java
new file mode 100644
index 00000000..8949e61b
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StringElementExtractor.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.reader;
+
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element;
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement;
+
+final class StringElementExtractor implements ExtractorWithEndpositionDetection {
+
+ static ExtractorWithEndpositionDetection startNewSting() {
+ return new StringElementExtractor();
+ }
+
+ private final StringBuilder value;
+
+ private StringElementExtractor() {
+ this(new StringBuilder());
+ }
+
+ private StringElementExtractor(final StringBuilder value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean hasNoInteresedIn(final int codePoint) {
+ return codePoint == UsefulCodepoints.QUOTATION_MARK;
+ }
+
+ @Override
+ public ExtractorWithEndpositionDetection append(final int codePoint) {
+ return new StringElementExtractor(value.appendCodePoint(codePoint));
+ }
+
+ @Override
+ public Element create() {
+ return StringElement.of(value.toString());
+ }
+}
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..45f1eb25
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+import java.util.logging.Level;
+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 List extractedRules = new ArrayList<>();
+ RuleExtractor ruleTokens = null;
+ for (int i = 0, length = rules.length(); i < length; i++) {
+ final int currentCodePoint = rules.codePointAt(i);
+ if (ruleTokens == null) {
+ if (Character.isLetter(currentCodePoint)) {
+ LOG.log(Level.FINER, "try to create first rule.");
+ ruleTokens = RuleExtractor.startNewRule(currentCodePoint);
+ }
+ } else {
+ if (ruleTokens.hasNoInteresedIn(currentCodePoint)) {
+ LOG.log(Level.FINER, "finished creating a rule.");
+ extractedRules.add(ruleTokens.create());
+ if (Character.isLetter(currentCodePoint)) {
+ LOG.log(Level.FINER, "try to create another rule.");
+ ruleTokens = RuleExtractor.startNewRule(currentCodePoint);
+ }
+ } else {
+ ruleTokens = ruleTokens.append(currentCodePoint);
+ }
+ }
+ }
+ if (ruleTokens != null) {
+ extractedRules.add(ruleTokens.create());
+ }
+ final RuleList result = RuleList.of(extractedRules);
+ 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/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..93a7cf45
--- /dev/null
+++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java
@@ -0,0 +1,38 @@
+/*
+ * 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 LEFT_PARENTHESIS = 0x28;
+ static final int RIGHT_PARENTHESIS = 0x29;
+ static final int ASTERISK = 0x2A;
+ static final int SOLIDUS = 0x2F;
+ 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/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/ConcatedStringTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatedStringTest.java
new file mode 100644
index 00000000..c3214c3c
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatedStringTest.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 ConcatedStringTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(ConcatedString.class).verify();
+ }
+}
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..7091e061
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.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 NumericCharacterTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(NumericCharacter.class).withOnlyTheseFields("value").verify();
+ }
+}
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..50f975ee
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.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 OptionalSequenceTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(OptionalSequence.class).verify();
+ }
+}
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..cea9c3ca
--- /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).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..97ab3fc7
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.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 SequenceGroupTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(SequenceGroup.class).verify();
+ }
+}
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..33b892f9
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.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 SpecificRepetitionTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(SpecificRepetition.class).verify();
+ }
+}
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..bfcd8341
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.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 StringElementTest {
+
+ @Test
+ void equalsContract() {
+ EqualsVerifier.forClass(StringElement.class).verify();
+ }
+}
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..a13c77b3
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.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 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()))
+ );
+ }
+}
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..0ec99407
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.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.element;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+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))
+ );
+ }
+}
diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractorTest.java
new file mode 100644
index 00000000..9483771a
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementsExtractorTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.both;
+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.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.SpecificRepetition;
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement;
+import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition;
+import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
+
+class ElementsExtractorTest {
+
+ @Test
+ void should_create_concated_chars() {
+ assertThat(
+ new ExtractableTestString("%d97 %d98 %d99\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ is(
+ Concatenation.of(
+ NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 97),
+ NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 98),
+ NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 99)
+ )
+ )
+ );
+ }
+
+ @Test
+ void should_create_concated_strings() {
+ assertThat(
+ new ExtractableTestString("\"a\" \"b\" \"c\"\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ is(Concatenation.of(StringElement.of("a"), StringElement.of("b"), StringElement.of("c")))
+ );
+ }
+
+ @Test
+ void should_create_concated_rule() {
+ assertThat(
+ new ExtractableTestString("foo bar foo\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ is(
+ Concatenation.of(
+ RuleReference.of(RuleName.of("foo")),
+ RuleReference.of(RuleName.of("bar")),
+ RuleReference.of(RuleName.of("foo"))
+ )
+ )
+ );
+ }
+
+ @Test
+ void should_create_alternatives() {
+ assertThat(
+ new ExtractableTestString("fu / bar\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ is(Alternative.of(RuleReference.of(RuleName.of("fu")), RuleReference.of(RuleName.of("bar"))))
+ );
+ }
+
+ @Test
+ void should_create_sequence_group() {
+ assertThat(
+ new ExtractableTestString("(foo / bar)\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ (Matcher) both(
+ is(
+ SequenceGroup.of(
+ Alternative.of(RuleReference.of(RuleName.of("foo")), RuleReference.of(RuleName.of("bar")))
+ )
+ )
+ )
+ .and(is(not(nullValue())))
+ );
+ }
+
+ @Test
+ void should_create_default_repetition() {
+ assertThat(
+ new ExtractableTestString("*foo\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ (Matcher) both(is(VariableRepetition.of(RuleReference.of(RuleName.of("foo"))))).and(is(not(nullValue())))
+ );
+ }
+
+ @Test
+ void should_create_at_least_repetition() {
+ assertThat(
+ new ExtractableTestString("1*foo\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ (Matcher) both(is(VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("foo")), 1)))
+ .and(is(not(nullValue())))
+ );
+ }
+
+ @Test
+ void should_create_at_most_repetition() {
+ assertThat(
+ new ExtractableTestString("*1foo\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ (Matcher) both(is(VariableRepetition.ofAtMost(RuleReference.of(RuleName.of("foo")), 1)))
+ .and(is(not(nullValue())))
+ );
+ }
+
+ @Test
+ void should_create_between_repetition() {
+ assertThat(
+ new ExtractableTestString("1*2foo\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ is(VariableRepetition.ofBetween(RuleReference.of(RuleName.of("foo")), 1, 2))
+ );
+ }
+
+ @Test
+ void should_create_specific_repetition() {
+ assertThat(
+ new ExtractableTestString("2foo\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ (Matcher) both(is(SpecificRepetition.of(RuleReference.of(RuleName.of("foo")), 2))).and(is(not(nullValue())))
+ );
+ }
+
+ @Test
+ void should_create_optional_sequence() {
+ assertThat(
+ new ExtractableTestString("[foo]\r\n")
+ .extractWith(
+ codepoint -> ExtractorEndsWithNewLine.of(ElementsExtractor.startElements().append(codepoint)),
+ 0x41
+ ),
+ (Matcher) both(is(OptionalSequence.of(RuleReference.of(RuleName.of("foo"))))).and(is(not(nullValue())))
+ );
+ }
+}
diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractableTestString.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractableTestString.java
new file mode 100644
index 00000000..01dde532
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractableTestString.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 static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.util.Objects;
+import java.util.function.IntFunction;
+
+final class ExtractableTestString {
+
+ private final String testString;
+
+ public ExtractableTestString(final String testString) {
+ this.testString = Objects.requireNonNull(testString);
+ }
+
+ public T extractWith(
+ final IntFunction> extratorGenerator,
+ final int endCodePoint
+ ) {
+ ExtractorWithEndpositionDetection tokens = null;
+ for (int i = 0; i < testString.length(); i++) {
+ if (tokens == null) {
+ tokens = extratorGenerator.apply(testString.codePointAt(i));
+ } else {
+ tokens = tokens.append(testString.codePointAt(i));
+ }
+ }
+ assert tokens != null;
+ assertThat(tokens.hasNoInteresedIn(endCodePoint), is(true));
+ return tokens.create();
+ }
+}
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..d52858c9
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.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 org.junit.jupiter.api.Test;
+
+class RuleExtractorTest {
+
+ @Test
+ void should_create_rule_from_oneline_without_commentary() {
+ //Note: a rule MUST end with crlf!! see rfc5234
+ final String ruleStr = "rule = rulename defined-as elements c-nl\r\n";
+
+ assertThat(
+ new ExtractableTestString(ruleStr).extractWith(codePoint -> RuleExtractor.startNewRule(codePoint), 65),
+ is(
+ 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"))
+ )
+ )
+ )
+ );
+ }
+
+ @Test
+ void should_create_rule_from_multiline_without_commentray() {
+ //Note: a rule MUST end with crlf!! see rfc5234. lf is already set by the formatting :)
+ final String ruleStr =
+ """
+ element = rulename / group / option /\r
+ char-val / num-val / prose-val\r
+ """;
+
+ assertThat(
+ new ExtractableTestString(ruleStr).extractWith(codePoint -> RuleExtractor.startNewRule(codePoint), 65),
+ is(
+ 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"))
+ )
+ )
+ )
+ );
+ }
+
+ @Test
+ void should_create_rule_from_string_with_commentary() {
+ //Note: a rule MUST end with crlf!! see rfc5234. lf is already set by the formatting :)
+ final String ruleStr =
+ """
+ rule = rulename defined-as elements c-nl\r
+ ; continues if next line starts\r
+ ; with white space\r
+ """;
+
+ assertThat(
+ new ExtractableTestString(ruleStr).extractWith(codePoint -> RuleExtractor.startNewRule(codePoint), 65),
+ is(
+ 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"))
+ )
+ )
+ )
+ );
+ }
+}
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..ac9d83ea
--- /dev/null
+++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNFTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.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.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
+ // concatenation = repetition *(1*c-wsp repetition)\r
+ // repetition = [repeat] element\r
+ // """
+ // )
+ .of(
+ """
+ rulelist = 1*( rule / (*c-wsp c-nl) )\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("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"))
+ // )
+ )
+ )
+ )
+ );
+ }
+}