diff --git a/pom.xml b/pom.xml index aa0ec438..cdffb201 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,8 @@ api vocabulary-spi - core + core + vocabulary-format-assertion diff --git a/vocabulary-format-assertion/pom.xml b/vocabulary-format-assertion/pom.xml new file mode 100644 index 00000000..26db662f --- /dev/null +++ b/vocabulary-format-assertion/pom.xml @@ -0,0 +1,87 @@ + + + + 4.0.0 + + + io.github.sebastian-toepfer.json-schema + json-schema + 0.1.0-SNAPSHOT + + + json-schema-vocabulary-format-assertion + Json Schema :: vocabulary :: format assertion + + + 0.4.0 + + + + + ${project.groupId} + json-schema-vocabulary-spi + ${project.version} + + + io.github.sebastian-toepfer.ddd + common + ${ddd.verison} + + + io.github.sebastian-toepfer.ddd + media-core + ${ddd.verison} + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.hamcrest + hamcrest + test + + + com.github.npathai + hamcrest-optional + test + + + nl.jqno.equalsverifier + equalsverifier + test + + + ${project.groupId} + json-schema-core + ${project.version} + test + + + + jakarta.json + jakarta.json-api + provided + + + + org.eclipse.parsson + parsson + test + + + diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java new file mode 100644 index 00000000..d06b58b2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertion.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion; + +import java.util.Objects; +import java.util.function.Predicate; + +public final class DefaultFormatAssertion implements FormatAssertion { + + private final String name; + private final Predicate predicate; + + public DefaultFormatAssertion(final String name, final Predicate predicate) { + this.name = Objects.requireNonNull(name); + this.predicate = Objects.requireNonNull(predicate); + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isValidFor(final String value) { + return predicate.test(value); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java new file mode 100644 index 00000000..76e9dbd0 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertion.java @@ -0,0 +1,30 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion; + +public interface FormatAssertion { + String name(); + + boolean isValidFor(String value); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.java new file mode 100644 index 00000000..8ff11ffd --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordType.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; + +import io.github.sebastiantoepfer.ddd.common.Media; +import io.github.sebastiantoepfer.jsonschema.InstanceType; +import io.github.sebastiantoepfer.jsonschema.JsonSchema; +import io.github.sebastiantoepfer.jsonschema.keyword.Annotation; +import io.github.sebastiantoepfer.jsonschema.keyword.Assertion; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.Json; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class FormatAssertionKeywordType implements KeywordType { + + private final List formats; + + public FormatAssertionKeywordType(final List formats) { + this.formats = List.copyOf(formats); + } + + @Override + public String name() { + return "format"; + } + + @Override + public Keyword createKeyword(final JsonSchema js) { + if ( + js.getValueType() == JsonValue.ValueType.OBJECT && + js.asJsonObject().containsKey(name()) && + js.asJsonObject().get(name()).getValueType() == JsonValue.ValueType.STRING + ) { + return createKeyword(js.asJsonObject().getString(name())); + } else { + throw new IllegalArgumentException("Value must be a string!"); + } + } + + private FormatAssertionKeyword createKeyword(final String formatName) { + return formats + .stream() + .filter(format -> format.name().equals(formatName)) + .map(FormatAssertionKeyword::new) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("format %s not supported!", formatName))); + } + + private class FormatAssertionKeyword implements Annotation, Assertion { + + private final FormatAssertion format; + + public FormatAssertionKeyword(final FormatAssertion format) { + this.format = format; + } + + @Override + public Collection categories() { + return List.of(KeywordCategory.ANNOTATION, KeywordCategory.ASSERTION); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public JsonValue valueFor(final JsonValue value) { + return Json.createValue(format.name()); + } + + @Override + public boolean isValidFor(final JsonValue instance) { + return !InstanceType.STRING.isInstance(instance) || format.isValidFor(((JsonString) instance).getString()); + } + + @Override + public > T printOn(final T media) { + return media.withValue(name(), format.name()); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java new file mode 100644 index 00000000..c76300ef --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabulary.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion; + +import io.github.sebastiantoepfer.jsonschema.Vocabulary; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies; +import java.net.URI; +import java.util.List; +import java.util.Optional; + +public class FormatAssertionVocabulary implements LazyVocabularies, Vocabulary { + + private final List keywords; + + public FormatAssertionVocabulary() { + this.keywords = + List.of(new FormatAssertionKeywordType(List.of(new DefaultFormatAssertion("email", s -> s.contains("@"))))); + } + + @Override + public Optional loadVocabularyWithId(final URI id) { + final Optional result; + if (id().equals(id)) { + result = Optional.of(this); + } else { + result = Optional.empty(); + } + return result; + } + + @Override + public URI id() { + return URI.create("https://json-schema.org/draft/2020-12/vocab/format-assertion"); + } + + @Override + public Optional findKeywordTypeByName(final String name) { + return keywords.stream().filter(k -> k.hasName(name)).findFirst(); + } +} diff --git a/vocabulary-format-assertion/src/main/java/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..0c9a0016 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRules.java @@ -0,0 +1,196 @@ +/* + * 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.ddd.common.Media; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Dimension; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; + +public enum CoreRules implements Element { + ALPHA() { + @Override + Element definition() { + return Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x41), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x5A) + ), + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x61), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7A) + ) + ); + } + }, + BIT() { + @Override + Element definition() { + return Alternative.of(StringElement.of("0"), StringElement.of("1")); + } + }, + CHAR() { + @Override + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x01), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7F) + ); + } + }, + CR() { + @Override + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0D); + } + }, + CRLF() { + @Override + Element definition() { + return Concatenation.of(CR, LF); + } + }, + CTL() { + @Override + Element definition() { + return Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x00), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x1F) + ), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7F) + ); + } + }, + DIGIT() { + @Override + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x30), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x39) + ); + } + }, + DQUOTE() { + @Override + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x22); + } + }, + HEXDIG() { + @Override + Element definition() { + return Alternative.of( + DIGIT, + StringElement.of("A"), + StringElement.of("B"), + StringElement.of("C"), + StringElement.of("D"), + StringElement.of("E"), + StringElement.of("F") + ); + } + }, + HTAB() { + @Override + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x09); + } + }, + LF() { + @Override + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x0A); + } + }, + LWSP() { + @Override + Element definition() { + return VariableRepetition.of(SequenceGroup.of(Concatenation.of(Alternative.of(WSP, CRLF), WSP))); + } + }, + OCTET() { + @Override + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x00), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0xFF) + ); + } + }, + SP() { + @Override + Element definition() { + return NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20); + } + }, + VCHAR() { + @Override + Element definition() { + return ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) + ); + } + }, + WSP { + @Override + Element definition() { + return Alternative.of(SP, HTAB); + } + }; + + public final RuleName asRuleName() { + return RuleName.of(name()); + } + + @Override + public final > T printOn(final T media) { + return Rule.of(asRuleName(), definition()).printOn(media).withValue("type", "corerule"); + } + + public final boolean isValidFor(final int codePoint) { + return isValidFor(ValidateableCodePoint.of(0, codePoint)); + } + + @Override + public final boolean isValidFor(final ValidateableCodePoint codePoint) { + return definition().isValidFor(codePoint); + } + + @Override + public final Dimension dimension() { + return definition().dimension(); + } + + abstract Element definition(); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/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..cb28b179 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/Rule.java @@ -0,0 +1,97 @@ +/* + * 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.ddd.common.Media; +import io.github.sebastiantoepfer.ddd.common.Printable; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +public final class Rule implements Printable { + + 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); + } + + Predicate asPredicate() { + return s -> + IntStream + .range(0, s.length()) + .boxed() + .map(i -> ValidateableCodePoint.of(i, s.codePointAt(i))) + .allMatch(elements::isValidFor); + } + + @Override + public > T printOn(final T media) { + return media.withValue("name", name).withValue("type", "rule").withValue("elements", elements); + } + + @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..55dc6825 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleList.java @@ -0,0 +1,84 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +import io.github.sebastiantoepfer.ddd.common.Printable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class RuleList implements Printable { + + 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 > T printOn(final T media) { + return media.withValue("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..87efed65 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Alternative.java @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import io.github.sebastiantoepfer.ddd.common.Media; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class Alternative implements Element { + + public static Alternative of(final Element left, final Element right, final Element... more) { + final List alternatives = new ArrayList<>(); + alternatives.add(left); + alternatives.add(right); + alternatives.addAll(Arrays.asList(more)); + return of(alternatives); + } + + public static Alternative of(final Collection alternatives) { + return new Alternative(alternatives); + } + + private final List alternatives; + + private Alternative(final Collection alternatives) { + this.alternatives = List.copyOf(alternatives); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "alternative").withValue("alternatives", List.copyOf(alternatives)); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return alternatives.stream().anyMatch(e -> e.isValidFor(codePoint)); + } + + @Override + public Dimension dimension() { + return alternatives.stream().map(Element::dimension).sorted().reduce(Dimension::expandTo).orElseThrow(); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 41 * hash + Objects.hashCode(this.alternatives); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Alternative other = (Alternative) obj; + return Objects.equals(this.alternatives, other.alternatives); + } + + @Override + public String toString() { + return "Alternative{" + "alternatives=" + alternatives + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java new file mode 100644 index 00000000..6ec49f07 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Concatenation.java @@ -0,0 +1,100 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class Concatenation implements Element { + + public static Concatenation of(final Element left, final Element right, final Element... more) { + final List concatenations = new ArrayList<>(); + concatenations.add(left); + concatenations.add(right); + concatenations.addAll(Arrays.asList(more)); + return of(concatenations); + } + + public static Concatenation of(final Collection alternatives) { + return new Concatenation(alternatives); + } + + private final List concatenations; + + private Concatenation(final Collection concatenations) { + this.concatenations = List.copyOf(concatenations); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "concatenation").withValue("concatenations", List.copyOf(concatenations)); + } + + @Override + public Dimension dimension() { + return concatenations.stream().map(Element::dimension).reduce(Dimension.zero(), Dimension::plus); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + result = new ListValidation(concatenations, Concatenation::of).isValidFor(codePoint); + } else { + result = false; + } + return result; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + Objects.hashCode(this.concatenations); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Concatenation other = (Concatenation) obj; + return Objects.equals(this.concatenations, other.concatenations); + } + + @Override + public String toString() { + return "Concatenation{" + "concatenation=" + concatenations + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java new file mode 100644 index 00000000..bcb9e197 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Dimension.java @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import static java.util.Comparator.comparingLong; + +public final class Dimension implements Comparable { + + private static final Dimension ZERO = Dimension.of(0); + private static final Dimension ONE = Dimension.of(1); + + public static Dimension zero() { + return ZERO; + } + + public static Dimension one() { + return ONE; + } + + public static Dimension of(final long size) { + return of(size, size); + } + + public static Dimension of(final long minSize, final long maxSize) { + if (minSize < 0) { + throw new IllegalArgumentException("minSize must be greather or equals than 0!"); + } + if (maxSize < minSize) { + throw new IllegalArgumentException("MaxSize must be greater or equals minSize!"); + } + return new Dimension(minSize, maxSize); + } + + private final long minSize; + private final long maxSize; + + private Dimension(final long minSize, final long maxSize) { + this.minSize = minSize; + this.maxSize = maxSize; + } + + public boolean isInRange(final ValidateableCodePoint codepoint) { + return codepoint.position() < maxSize; + } + + Dimension expandTo(final Dimension newMax) { + return of(Math.min(minSize, newMax.minSize), Math.max(maxSize, newMax.maxSize)); + } + + Dimension plus(final Dimension toAdd) { + return of(minSize + toAdd.minSize, maxSize + toAdd.maxSize); + } + + Dimension multipliesBy(final int multiplier) { + return of(minSize * multiplier, maxSize * multiplier); + } + + @Override + public int compareTo(final Dimension t) { + return comparingLong(Dimension::length).thenComparing(comparingLong(Dimension::minSize)).compare(this, t); + } + + private long minSize() { + return minSize; + } + + long length() { + return maxSize; + } + + @Override + public String toString() { + return "Dimension{" + "minSize=" + minSize + ", maxSize=" + maxSize + '}'; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + (int) (this.minSize ^ (this.minSize >>> 32)); + hash = 53 * hash + (int) (this.maxSize ^ (this.maxSize >>> 32)); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Dimension other = (Dimension) obj; + if (this.minSize != other.minSize) { + return false; + } + return this.maxSize == other.maxSize; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java new file mode 100644 index 00000000..efb83da4 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/Element.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import io.github.sebastiantoepfer.ddd.common.Printable; + +public interface Element extends Printable { + boolean isValidFor(ValidateableCodePoint codePoint); + + default Dimension dimension() { + return Dimension.one(); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java new file mode 100644 index 00000000..8aa671ac --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ListValidation.java @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +class ListValidation { + + private final List elements; + private final Function, Element> newElement; + + public ListValidation(final List elements, final Function, Element> newElement) { + this.elements = List.copyOf(elements); + this.newElement = Objects.requireNonNull(newElement); + } + + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + final Element first = elements.get(0); + if (first.dimension().isInRange(codePoint)) { + result = first.isValidFor(codePoint); + } else { + result = + newElement + .apply(elements.subList(1, elements.size())) + .isValidFor(codePoint.repositionBackBy(first.dimension())); + } + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/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..861f4ecb --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacter.java @@ -0,0 +1,114 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +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 ValidateableCodePoint codePoint) { + return codePoint.isEqualsTo(value); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "num-val").withValue("base", base.name()).withValue("value", value); + } + + boolean lessThanOrEquals(final ValidateableCodePoint codePoint) { + return codePoint.isGreaterOrEqualsThan(value); + } + + boolean greatherThaneOrEquals(final ValidateableCodePoint codePoint) { + return codePoint.isLessOrEqualsThan(value); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + Objects.hashCode(value); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NumericCharacter other = (NumericCharacter) obj; + return value == other.value; + } + + @Override + public String toString() { + return "NumericCharacter{" + "base=" + base + ", value=" + value + '}'; + } + + public enum BASE { + BINARY('b', 2), + DECIMAL('d', 10), + HEXADECIMAL('x', 16); + + private final char baseShortName; + private final int radix; + + private BASE(final char baseShortName, final int base) { + this.baseShortName = baseShortName; + this.radix = base; + } + + public Integer convert(final String value) { + return Integer.valueOf(value, radix); + } + + public static BASE findByShortName(final char baseChar) { + return Arrays + .stream(BASE.values()) + .filter(b -> b.baseShortName == baseChar) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java new file mode 100644 index 00000000..34de4c98 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequence.java @@ -0,0 +1,92 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class OptionalSequence implements Element { + + public static OptionalSequence of(final Element optionalElement) { + return of(List.of(Objects.requireNonNull(optionalElement))); + } + + public static OptionalSequence of(final Collection optionalElements) { + return new OptionalSequence(optionalElements); + } + + private final List optionalElements; + + public OptionalSequence(final Collection optionalElements) { + this.optionalElements = List.copyOf(optionalElements); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "option").withValue("optionals", List.copyOf(optionalElements)); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return true; + } + + @Override + public Dimension dimension() { + return Dimension + .zero() + .expandTo( + optionalElements.stream().map(Element::dimension).sorted().reduce(Dimension.zero(), Dimension::plus) + ); + } + + @Override + public String toString() { + return "OptionalSequence{" + "optionalElements=" + optionalElements + '}'; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + Objects.hashCode(this.optionalElements); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OptionalSequence other = (OptionalSequence) obj; + return Objects.equals(this.optionalElements, other.optionalElements); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java new file mode 100644 index 00000000..24c7170f --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleName.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import io.github.sebastiantoepfer.ddd.common.Media; +import io.github.sebastiantoepfer.ddd.common.Printable; +import java.util.Locale; +import java.util.Objects; + +public final class RuleName implements Printable { + + public static RuleName of(final String name) { + return new RuleName(name); + } + + private final String name; + + private RuleName(final String name) { + this.name = Objects.requireNonNull(name); + } + + String name() { + return name; + } + + @Override + public > T printOn(final T media) { + return media.withValue("name", name).withValue("type", "rulename"); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + Objects.hashCode(name.toLowerCase(Locale.US)); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final RuleName other = (RuleName) obj; + return Objects.equals(this.name.toLowerCase(Locale.US), other.name.toLowerCase(Locale.US)); + } + + @Override + public String toString() { + return "RuleName{" + "name=" + name + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java new file mode 100644 index 00000000..6703388c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReference.java @@ -0,0 +1,86 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +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 ValidateableCodePoint codePoint) { + return ruleNameAsStringElement().isValidFor(codePoint); + } + + @Override + public Dimension dimension() { + return ruleNameAsStringElement().dimension(); + } + + Element ruleNameAsStringElement() { + return StringElement.of(name.name()); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "rule-ref").withValue("name", name); + } + + @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..b454c605 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroup.java @@ -0,0 +1,104 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import io.github.sebastiantoepfer.ddd.common.Media; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public final class SequenceGroup implements Element { + + public static SequenceGroup of(final Element term) { + return of(List.of(Objects.requireNonNull(term))); + } + + public static SequenceGroup of(final Element first, final Element second, final Element... more) { + final List elements = new ArrayList<>(); + elements.add(Objects.requireNonNull(first)); + elements.add(Objects.requireNonNull(second)); + elements.addAll(Arrays.asList(more)); + return of(elements); + } + + public static SequenceGroup of(final Collection elements) { + return new SequenceGroup(elements); + } + + private final List elements; + + private SequenceGroup(final Collection elements) { + this.elements = List.copyOf(elements); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "group").withValue("elements", List.copyOf(elements)); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + result = new ListValidation(elements, SequenceGroup::of).isValidFor(codePoint); + } else { + result = false; + } + return result; + } + + @Override + public Dimension dimension() { + return elements.stream().map(Element::dimension).reduce(Dimension.zero(), Dimension::plus); + } + + @Override + public String toString() { + return "SequenceGroup{" + "elements=" + elements + '}'; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 17 * hash + Objects.hashCode(this.elements); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SequenceGroup other = (SequenceGroup) obj; + return Objects.equals(this.elements, other.elements); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java new file mode 100644 index 00000000..7b537758 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetition.java @@ -0,0 +1,102 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +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 > T printOn(final T media) { + return media + .withValue("type", "fix-repetition") + .withValue("repeat", occurences) + .withValue("element", elementToRepeat); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + if (elementToRepeat.dimension().isInRange(codePoint)) { + result = elementToRepeat.isValidFor(codePoint); + } else { + result = + of(elementToRepeat, occurences).isValidFor(codePoint.repositionBackBy(elementToRepeat.dimension())); + } + } else { + result = false; + } + return result; + } + + @Override + public Dimension dimension() { + return elementToRepeat.dimension().multipliesBy(occurences); + } + + @Override + 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..6ef3cb5f --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElement.java @@ -0,0 +1,88 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import io.github.sebastiantoepfer.ddd.common.Media; +import java.util.Locale; +import java.util.Objects; + +public final class StringElement implements Element { + + public static StringElement of(final String value) { + return new StringElement(value); + } + + private final String value; + + private StringElement(final String value) { + this.value = Objects.requireNonNull(value); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + return ( + dimension().isInRange(codePoint) && + (codePoint.isEqualsTo(value.codePointAt(codePoint.position())) || + codePoint.isUpperCaseOf(value.codePointAt(codePoint.position())) || + codePoint.isLowerCaseOf(value.codePointAt(codePoint.position()))) + ); + } + + @Override + public Dimension dimension() { + return Dimension.of(value.length()); + } + + @Override + public > T printOn(final T media) { + return media.withValue("type", "char-val").withValue("value", value); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 17 * hash + Objects.hashCode(this.value.toLowerCase(Locale.US)); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final StringElement other = (StringElement) obj; + return Objects.equals(this.value.toLowerCase(Locale.US), other.value.toLowerCase(Locale.US)); + } + + @Override + public String toString() { + return "StringElement{" + "value=" + value + '}'; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java new file mode 100644 index 00000000..4e34365a --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePoint.java @@ -0,0 +1,107 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +public final class ValidateableCodePoint { + + public static ValidateableCodePoint of(final int position, final int value) { + if (position < 0) { + throw new IllegalArgumentException("position must be greather than 0!"); + } + if (value < 0) { + throw new IllegalArgumentException("value must be greather than 0!"); + } + return new ValidateableCodePoint(position, value); + } + + private final int position; + private final int value; + + private ValidateableCodePoint(final int position, final int value) { + this.position = position; + this.value = value; + } + + public ValidateableCodePoint repositionBackBy(final Dimension before) { + return repositionTo(position - (int) before.length()); + } + + public ValidateableCodePoint repositionBackBy(final int offset) { + return repositionTo(position - offset); + } + + public ValidateableCodePoint repositionTo(final int newPosition) { + return of(newPosition, value); + } + + public int position() { + return position; + } + + public boolean isEqualsTo(final int codePoint) { + return codePoint == value; + } + + public boolean isUpperCaseOf(final int codePoint) { + return Character.toUpperCase(codePoint) == value; + } + + public boolean isLowerCaseOf(final int codePoint) { + return Character.toLowerCase(codePoint) == value; + } + + public boolean isGreaterOrEqualsThan(final int codePoint) { + return codePoint <= value; + } + + public boolean isLessOrEqualsThan(final int codePoint) { + return value <= codePoint; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 71 * hash + this.position; + hash = 71 * hash + this.value; + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ValidateableCodePoint other = (ValidateableCodePoint) obj; + if (this.position != other.position) { + return false; + } + return this.value == other.value; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java new file mode 100644 index 00000000..4538f367 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternatives.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import io.github.sebastiantoepfer.ddd.common.Media; +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 > T printOn(final T media) { + return media.withValue("type", "val-range").withValue("from", start).withValue("to", end); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint 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..ba998dc0 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetition.java @@ -0,0 +1,145 @@ +/* + * 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 io.github.sebastiantoepfer.ddd.common.Media; +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 > T printOn(final T media) { + final T result; + if (minOccurrences > 0 && maxOccurrences < Integer.MAX_VALUE) { + result = media.withValue("atLeast", minOccurrences).withValue("atMost", maxOccurrences); + } else if (minOccurrences > 0) { + result = media.withValue("atLeast", minOccurrences); + } else if (maxOccurrences < Integer.MAX_VALUE) { + result = media.withValue("atMost", maxOccurrences); + } else { + result = media; + } + return result.withValue("type", "repetition").withValue("element", element); + } + + @Override + public boolean isValidFor(final ValidateableCodePoint codePoint) { + final boolean result; + if (dimension().isInRange(codePoint)) { + if (element.dimension().isInRange(codePoint)) { + result = element.isValidFor(codePoint); + } else { + final int newMinOccurrences = Math.min(minOccurrences, 1); + result = + ofBetween(element, newMinOccurrences, Math.max(newMinOccurrences, maxOccurrences - 1)) + .isValidFor(codePoint.repositionBackBy(element.dimension())); + } + } else { + result = false; + } + return result; + } + + @Override + public Dimension dimension() { + return element + .dimension() + .multipliesBy(minOccurrences) + .expandTo(element.dimension().multipliesBy(maxOccurrences)); + } + + @Override + 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..0b72a9a2 --- /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 extends AutoCloseable { + RuleList rules(); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java new file mode 100644 index 00000000..32cbc47e --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFs.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +public final class ABNFs { + + public static ABNF of(final CharSequence sequence) { + return TextABNF.of(sequence); + } + + public static ABNF of(final InputStream is, final Charset cs) { + return of(new InputStreamReader(is, cs)); + } + + public static ABNF of(final Reader input) { + return StreambasedABNF.of(input); + } + + private ABNFs() {} +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java new file mode 100644 index 00000000..5f83b621 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/AlternativeExtractor.java @@ -0,0 +1,99 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.SOLIDUS; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +class AlternativeExtractor implements Extractor, ExtractorOwner { + + static Extractor of( + final ExtractorOwner owner, + final Element element, + final ElementEndDetector endOfElementDelimiter + ) { + return new AlternativeExtractor(owner, List.of(element), endOfElementDelimiter, RepetitionExtractor::of); + } + + private final ExtractorOwner owner; + private final List elements; + private final ElementEndDetector endOfElementDetector; + private final Function newElement; + + private AlternativeExtractor( + final ExtractorOwner owner, + final Collection elements, + final ElementEndDetector endOfElementDelimiter, + final Function newElement + ) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + this.endOfElementDetector = Objects.requireNonNull(endOfElementDelimiter); + this.newElement = Objects.requireNonNull(newElement); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + final ElementEndDetector currentEndOfElementDetector = endOfElementDetector.append(codePoint); + if (currentEndOfElementDetector.isEndReached()) { + result = currentEndOfElementDetector.applyTo(owner.imDone(asCreator())); + } else if (Character.isWhitespace(codePoint)) { + result = new AlternativeExtractor(owner, elements, currentEndOfElementDetector, newElement); + } else if (codePoint == SOLIDUS) { + result = new AlternativeExtractor(owner, elements, currentEndOfElementDetector, RepetitionExtractor::of); + } else { + result = newElement.apply(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final Collection newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new AlternativeExtractor(owner, newElements, endOfElementDetector, p -> owner.imDone(p.asCreator())); + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(Alternative.of(elements)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java new file mode 100644 index 00000000..5f22737e --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CharValExtractor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.QUOTATION_MARK; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import java.util.Objects; + +class CharValExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new CharValExtractor(owner, ""); + } + + private final ExtractorOwner owner; + private final String value; + + private CharValExtractor(final ExtractorOwner owner, final String value) { + this.owner = Objects.requireNonNull(owner); + this.value = Objects.requireNonNull(value); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == QUOTATION_MARK) { + result = owner.imDone(asCreator()); + } else { + result = new CharValExtractor(owner, value.concat(Character.toString(codePoint))); + } + return result; + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(StringElement.of(value)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java new file mode 100644 index 00000000..e94e7bfe --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/CodePointBasedElementEndDelimiter.java @@ -0,0 +1,54 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +class CodePointBasedElementEndDelimiter implements ElementEndDetector { + + private final int stop; + private final boolean reachStop; + + CodePointBasedElementEndDelimiter(final int stop) { + this(stop, false); + } + + private CodePointBasedElementEndDelimiter(final int stop, final boolean reachStop) { + this.stop = stop; + this.reachStop = reachStop; + } + + @Override + public ElementEndDetector append(final int codePoint) { + return new CodePointBasedElementEndDelimiter(stop, codePoint == stop); + } + + @Override + public boolean isEndReached() { + return reachStop; + } + + @Override + public Extractor applyTo(final Extractor imDone) { + return imDone.append(stop); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java new file mode 100644 index 00000000..51a6db55 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractor.java @@ -0,0 +1,106 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.SOLIDUS; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class ConcatenationExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new ConcatenationExtractor(owner, List.of(), new NewLineDetector()); + } + + static Extractor of(final ExtractorOwner owner, final int stop) { + return new ConcatenationExtractor(owner, List.of(), new CodePointBasedElementEndDelimiter(stop)); + } + + private final ExtractorOwner owner; + private final List elements; + private final ElementEndDetector endOfElementDelimiter; + + private ConcatenationExtractor( + final ExtractorOwner owner, + final Collection elements, + final ElementEndDetector endOfElementDelimiter + ) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + this.endOfElementDelimiter = endOfElementDelimiter; + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + final ElementEndDetector currentEndOfElementDetector = endOfElementDelimiter.append(codePoint); + if (currentEndOfElementDetector.isEndReached()) { + result = currentEndOfElementDetector.applyTo(owner.imDone(asCreator())); + } else if (Character.isWhitespace(codePoint)) { + result = new ConcatenationExtractor(owner, elements, currentEndOfElementDetector); + } else if (codePoint == SOLIDUS) { + result = + new ConcationTypeSwitch(owner, currentEndOfElementDetector, elements) + .switchTo( + (o, d, e) -> new ConcatenationExtractor(o, e, d), + (o, d, e) -> AlternativeExtractor.of(o, e, d) + ); + } else { + result = RepetitionExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + final Element result; + if (elements.size() == 1) { + result = elements.iterator().next(); + } else { + result = Concatenation.of(elements); + } + return cls.cast(result); + } + }; + } + + @Override + public Extractor imDone(final Creator creator) { + final Collection newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new ConcatenationExtractor(owner, newElements, endOfElementDelimiter); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java new file mode 100644 index 00000000..40f517ca --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcationTypeSwitch.java @@ -0,0 +1,56 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +final class ConcationTypeSwitch { + + private final ExtractorOwner owner; + private final ElementEndDetector endOfElementDetector; + private final List elements; + + public ConcationTypeSwitch( + final ExtractorOwner owner, + final ElementEndDetector endOfElementDetector, + final List elements + ) { + this.owner = Objects.requireNonNull(owner); + this.endOfElementDetector = Objects.requireNonNull(endOfElementDetector); + this.elements = List.copyOf(elements); + } + + public Extractor switchTo(final ExtractorOwnerFactory ownerFactory, final ExtractorFactory targetFactory) { + final List newElements = new ArrayList<>(elements); + final Element element = newElements.remove(newElements.size() - 1); + return targetFactory.create( + ownerFactory.create(owner, endOfElementDetector, newElements), + endOfElementDetector, + element + ); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java new file mode 100644 index 00000000..5ba550ed --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Creator.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +interface Creator { + T createAs(Class cls); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java new file mode 100644 index 00000000..226c3970 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementEndDetector.java @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +interface ElementEndDetector extends Extractor { + @Override + ElementEndDetector append(int codePoint); + + boolean isEndReached(); + + Extractor applyTo(Extractor imDone); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java new file mode 100644 index 00000000..666ba2c2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractor.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.LEFT_PARENTHESIS; +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.LEFT_SQUARE_BRACKET; +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.PERCENT_SIGN; +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.QUOTATION_MARK; + +import java.util.Objects; + +final class ElementExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new ElementExtractor(owner); + } + + private final ExtractorOwner owner; + + private ElementExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleReferenceExtractor.of(owner).append(codePoint); + } else if (codePoint == LEFT_PARENTHESIS) { + result = GroupExtractor.of(owner); + } else if (codePoint == LEFT_SQUARE_BRACKET) { + result = OptionExtractor.of(owner); + } else if (codePoint == QUOTATION_MARK) { + result = CharValExtractor.of(owner); + } else if (codePoint == PERCENT_SIGN) { + result = NumValExtractor.of(owner); + } else { + result = this; + } + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java new file mode 100644 index 00000000..4bd2d757 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/Extractor.java @@ -0,0 +1,32 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +interface Extractor { + Extractor append(int codePoint); + + default Creator finish() { + throw new IllegalStateException("current extractor can not be finished!"); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java new file mode 100644 index 00000000..b918e195 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorFactory.java @@ -0,0 +1,31 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; + +@FunctionalInterface +interface ExtractorFactory { + Extractor create(ExtractorOwner owner, ElementEndDetector currentEndOfElementDetector, Element element); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java new file mode 100644 index 00000000..b27bb5bb --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwner.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +interface ExtractorOwner { + Extractor imDone(Creator creator); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java new file mode 100644 index 00000000..87398bd4 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ExtractorOwnerFactory.java @@ -0,0 +1,32 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import java.util.List; + +@FunctionalInterface +interface ExtractorOwnerFactory { + ExtractorOwner create(ExtractorOwner owner, ElementEndDetector currentEndOfElementDetector, List elements); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java new file mode 100644 index 00000000..e2cbc9f7 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractor.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.RIGHT_PARENTHESIS; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +final class GroupExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new GroupExtractor(owner, List.of()); + } + + private final ExtractorOwner owner; + private final List elements; + + private GroupExtractor(final ExtractorOwner owner, final Collection elements) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == RIGHT_PARENTHESIS) { + result = owner.imDone(asCreator()); + } else { + result = ConcatenationExtractor.of(this, RIGHT_PARENTHESIS).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final List newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new GroupExtractor(owner, newElements); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(SequenceGroup.of(elements)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java new file mode 100644 index 00000000..93b3dbd2 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetector.java @@ -0,0 +1,69 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +class NewLineDetector implements ElementEndDetector { + + private final boolean cr; + private final boolean lf; + private final int lastCodePoint; + + public NewLineDetector() { + this(false, false, 0); + } + + private NewLineDetector(final boolean cr, final boolean lf, final int lastCodePoint) { + this.cr = cr; + this.lf = lf; + this.lastCodePoint = lastCodePoint; + } + + @Override + public NewLineDetector append(final int codePoint) { + final NewLineDetector result; + if (lf) { + result = + new NewLineDetector( + cr && !Character.isWhitespace(codePoint), + !Character.isWhitespace(codePoint), + codePoint + ); + } else if (cr) { + result = new NewLineDetector(codePoint == '\n', codePoint == '\n', codePoint); + } else { + result = new NewLineDetector(codePoint == '\r', false, codePoint); + } + return result; + } + + @Override + public boolean isEndReached() { + return cr && lf && !Character.isWhitespace(lastCodePoint); + } + + @Override + public Extractor applyTo(final Extractor imDone) { + return imDone.append('\r').append('\n').append(lastCodePoint); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java new file mode 100644 index 00000000..888ab417 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractor.java @@ -0,0 +1,173 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import java.util.Objects; + +class NumValExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new NumValExtractor(owner); + } + + private final ExtractorOwner owner; + + private NumValExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final NumericCharacter.BASE base = NumericCharacter.BASE.findByShortName(Character.toChars(codePoint)[0]); + final CoreRules rule = + switch (base) { + case BINARY -> CoreRules.BIT; + case DECIMAL -> CoreRules.DIGIT; + case HEXADECIMAL -> CoreRules.HEXDIG; + default -> throw new AssertionError(); + }; + return new SpecificNumValExtractor(owner, base, rule); + } + + private static class SpecificNumValExtractor implements Extractor { + + private final ExtractorOwner owner; + private final NumericCharacter.BASE base; + private final CoreRules rule; + private final String value; + + public SpecificNumValExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule + ) { + this(owner, base, rule, "0"); + } + + public SpecificNumValExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule, + final String value + ) { + this.owner = Objects.requireNonNull(owner); + this.base = Objects.requireNonNull(base); + this.rule = Objects.requireNonNull(rule); + this.value = Objects.requireNonNull(value); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == UsefulCodepoints.HYPHEN_MINUS) { + result = new ValueRangeExtractor(owner, base, rule, value); + } else if (Character.isWhitespace(codePoint) || !rule.isValidFor(ValidateableCodePoint.of(0, codePoint))) { + result = owner.imDone(asCreator()).append(codePoint); + } else { + result = new SpecificNumValExtractor(owner, base, rule, value.concat(Character.toString(codePoint))); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(NumericCharacter.of(base, base.convert(value))); + } + }; + } + } + + private static class ValueRangeExtractor implements Extractor { + + private final ExtractorOwner owner; + private final NumericCharacter.BASE base; + private final CoreRules rule; + private final String start; + private final String end; + + public ValueRangeExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule, + final String start + ) { + this(owner, base, rule, start, "0"); + } + + private ValueRangeExtractor( + final ExtractorOwner owner, + final NumericCharacter.BASE base, + final CoreRules rule, + final String start, + final String end + ) { + this.owner = Objects.requireNonNull(owner); + this.base = Objects.requireNonNull(base); + this.rule = Objects.requireNonNull(rule); + this.start = Objects.requireNonNull(start); + this.end = Objects.requireNonNull(end); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isWhitespace(codePoint) || !rule.isValidFor(ValidateableCodePoint.of(0, codePoint))) { + result = owner.imDone(asCreator()).append(codePoint); + } else { + result = new ValueRangeExtractor(owner, base, rule, start, end.concat(Character.toString(codePoint))); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + ValueRangeAlternatives.of( + NumericCharacter.of(base, base.convert(start)), + NumericCharacter.of(base, base.convert(end)) + ) + ); + } + }; + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java new file mode 100644 index 00000000..08738749 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractor.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.RIGHT_SQUARE_BRACKET; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +class OptionExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new OptionExtractor(owner, List.of()); + } + + private final ExtractorOwner owner; + private final List elements; + + private OptionExtractor(final ExtractorOwner owner, final Collection elements) { + this.owner = Objects.requireNonNull(owner); + this.elements = List.copyOf(elements); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == RIGHT_SQUARE_BRACKET) { + result = owner.imDone(asCreator()); + } else { + result = ConcatenationExtractor.of(this, RIGHT_SQUARE_BRACKET).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final Collection newElements = new ArrayList<>(elements); + newElements.add(creator.createAs(Element.class)); + return new OptionExtractor(owner, newElements); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(OptionalSequence.of(elements)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java new file mode 100644 index 00000000..6b8853af --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractor.java @@ -0,0 +1,242 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.ASTERISK; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SpecificRepetition; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import java.util.Objects; + +class RepetitionExtractor implements Extractor { + + static Extractor of(final ExtractorOwner owner) { + return new RepetitionExtractor(owner); + } + + private final ExtractorOwner owner; + + private RepetitionExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + Extractor result; + if (Character.isDigit(codePoint)) { + result = new SpecificRepetitionExtractor(owner, Character.toString(codePoint)); + } else if (codePoint == ASTERISK) { + result = new UnspecificRepetitionExtractor(owner); + } else { + result = ElementExtractor.of(owner).append(codePoint); + } + return result; + } + + private static class SpecificRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String repeat; + + public SpecificRepetitionExtractor(final ExtractorOwner owner, final String repeat) { + this.owner = Objects.requireNonNull(owner); + this.repeat = Objects.requireNonNull(repeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == ASTERISK) { + result = new AtLeastRepetitionExtractor(owner, repeat); + } else if (Character.isDigit(codePoint)) { + result = new SpecificRepetitionExtractor(owner, repeat.concat(Character.toString(codePoint))); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + SpecificRepetition.of(creator.createAs(Element.class), Integer.parseInt(repeat)) + ); + } + } + ); + } + } + + private static class AtLeastRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String minRepeat; + + private AtLeastRepetitionExtractor(final ExtractorOwner owner, final String minRepeat) { + this.owner = Objects.requireNonNull(owner); + this.minRepeat = Objects.requireNonNull(minRepeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = new BetweenRepetitionExtractor(owner, minRepeat, Character.toString(codePoint)); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + VariableRepetition.ofAtLeast(creator.createAs(Element.class), Integer.parseInt(minRepeat)) + ); + } + } + ); + } + } + + private static class UnspecificRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + + public UnspecificRepetitionExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = new AtMostRepetitionExtractor(owner, Character.toString(codePoint)); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(VariableRepetition.of(creator.createAs(Element.class))); + } + } + ); + } + } + + private static class AtMostRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String maxRepeat; + + public AtMostRepetitionExtractor(final ExtractorOwner owner, final String maxRepeat) { + this.owner = Objects.requireNonNull(owner); + this.maxRepeat = Objects.requireNonNull(maxRepeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = new AtMostRepetitionExtractor(owner, maxRepeat.concat(Character.toString(codePoint))); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + VariableRepetition.ofAtMost(creator.createAs(Element.class), Integer.parseInt(maxRepeat)) + ); + } + } + ); + } + } + + private static class BetweenRepetitionExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final String minRepeat; + private final String maxRepeat; + + public BetweenRepetitionExtractor(final ExtractorOwner owner, final String minRepeat, final String maxRepeat) { + this.owner = Objects.requireNonNull(owner); + this.minRepeat = Objects.requireNonNull(minRepeat); + this.maxRepeat = Objects.requireNonNull(maxRepeat); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isDigit(codePoint)) { + result = + new BetweenRepetitionExtractor(owner, minRepeat, maxRepeat.concat(Character.toString(codePoint))); + } else { + result = ElementExtractor.of(this).append(codePoint); + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast( + VariableRepetition.ofBetween( + creator.createAs(Element.class), + Integer.parseInt(minRepeat), + Integer.parseInt(maxRepeat) + ) + ); + } + } + ); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java new file mode 100644 index 00000000..5cd2d66c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractor.java @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader.UsefulCodepoints.EQUALS_SIGN; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import java.util.Objects; + +final class RuleExtractor implements Extractor, ExtractorOwner { + + public static Extractor of(final ExtractorOwner owner) { + return new RuleExtractor(owner); + } + + private final ExtractorOwner owner; + + private RuleExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleNameExtractor.of(this).append(codePoint); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return new DefinedAsExtractor(owner, creator.createAs(RuleName.class)); + } + + private static class DefinedAsExtractor implements Extractor, ExtractorOwner { + + private final ExtractorOwner owner; + private final RuleName ruleName; + + public DefinedAsExtractor(final ExtractorOwner owner, final RuleName ruleName) { + this.owner = Objects.requireNonNull(owner); + this.ruleName = Objects.requireNonNull(ruleName); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (codePoint == EQUALS_SIGN) { + result = ConcatenationExtractor.of(this); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(Rule.of(ruleName, creator.createAs(Element.class))); + } + } + ); + } + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java new file mode 100644 index 00000000..0c3c30f5 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractor.java @@ -0,0 +1,74 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import java.util.ArrayList; +import java.util.List; + +final class RuleListExtractor implements Extractor, ExtractorOwner { + + public static Extractor of() { + return new RuleListExtractor(); + } + + private final List rules; + + private RuleListExtractor() { + this(List.of()); + } + + private RuleListExtractor(final List rules) { + this.rules = List.copyOf(rules); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleExtractor.of(this).append(codePoint); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + final List newRules = new ArrayList<>(rules); + newRules.add(creator.createAs(Rule.class)); + return new RuleListExtractor(newRules); + } + + @Override + public Creator finish() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(RuleList.of(rules)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java new file mode 100644 index 00000000..6869d1b7 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractor.java @@ -0,0 +1,73 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import java.util.Objects; + +final class RuleNameExtractor implements Extractor { + + public static Extractor of(final ExtractorOwner owner) { + return new RuleNameExtractor(owner); + } + + private final ExtractorOwner owner; + private final String name; + + private RuleNameExtractor(final ExtractorOwner owner) { + this(owner, ""); + } + + private RuleNameExtractor(final ExtractorOwner owner, final String name) { + this.owner = Objects.requireNonNull(owner); + this.name = Objects.requireNonNull(name); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (name.isEmpty() && !Character.isLetter(codePoint)) { + throw new IllegalArgumentException("rulename must start with a letter!"); + } else if (Character.isLetterOrDigit(codePoint) || codePoint == UsefulCodepoints.HYPHEN_MINUS) { + result = new RuleNameExtractor(owner, name.concat(Character.toString(codePoint))); + } else { + result = owner.imDone(asCreator()).append(codePoint); + } + return result; + } + + @Override + public Creator finish() { + return owner.imDone(asCreator()).finish(); + } + + private Creator asCreator() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(RuleName.of(name)); + } + }; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java new file mode 100644 index 00000000..e8088920 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleReferenceExtractor.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import java.util.Objects; + +class RuleReferenceExtractor implements Extractor, ExtractorOwner { + + static Extractor of(final ExtractorOwner owner) { + return new RuleReferenceExtractor(owner); + } + + private final ExtractorOwner owner; + + private RuleReferenceExtractor(final ExtractorOwner owner) { + this.owner = Objects.requireNonNull(owner); + } + + @Override + public Extractor append(final int codePoint) { + final Extractor result; + if (Character.isAlphabetic(codePoint)) { + result = RuleNameExtractor.of(this).append(codePoint); + } else { + result = this; + } + return result; + } + + @Override + public Extractor imDone(final Creator creator) { + return owner.imDone( + new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(RuleReference.of(creator.createAs(RuleName.class))); + } + } + ); + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java new file mode 100644 index 00000000..1f8c2214 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNF.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import java.io.IOException; +import java.io.Reader; +import java.util.Objects; +import java.util.logging.Logger; + +final class StreambasedABNF implements ABNF { + + public static ABNF of(final Reader reader) { + return new StreambasedABNF(reader); + } + + private static final Logger LOG = Logger.getLogger(StreambasedABNF.class.getName()); + + private final Reader reader; + + private StreambasedABNF(final Reader reader) { + this.reader = Objects.requireNonNull(reader); + } + + @Override + public void close() throws IOException { + reader.close(); + } + + @Override + public RuleList rules() { + LOG.entering(StreambasedABNF.class.getName(), "rules"); + final RuleList result; + try { + Extractor extractor = RuleListExtractor.of(); + int codePoint; + while ((codePoint = reader.read()) != -1) { + extractor = extractor.append(codePoint); + } + result = extractor.finish().createAs(RuleList.class); + } catch (IOException ex) { + final IllegalArgumentException thrown = new IllegalArgumentException(ex); + LOG.throwing(StreambasedABNF.class.getName(), "rules", thrown); + throw thrown; + } + LOG.exiting(TextABNF.class.getName(), "rules", result); + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java new file mode 100644 index 00000000..d4d4b85f --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TextABNF.java @@ -0,0 +1,60 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import java.util.logging.Logger; + +final class TextABNF implements ABNF { + + private static final Logger LOG = Logger.getLogger(TextABNF.class.getName()); + + public static TextABNF of(final CharSequence rules) { + return new TextABNF(rules); + } + + private final CharSequence rules; + + private TextABNF(final CharSequence rules) { + this.rules = rules; + } + + @Override + public void close() { + //nothing to do! + } + + @Override + public RuleList rules() { + LOG.entering(TextABNF.class.getName(), "rules"); + final RuleList result = rules + .codePoints() + .boxed() + .reduce(RuleListExtractor.of(), Extractor::append, (l, r) -> null) + .finish() + .createAs(RuleList.class); + LOG.exiting(TextABNF.class.getName(), "rules", result); + return result; + } +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java new file mode 100644 index 00000000..034bfc5c --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/TokenCreator.java @@ -0,0 +1,28 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +interface TokenCreator { + T create(); +} diff --git a/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java new file mode 100644 index 00000000..f56f7d9d --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/UsefulCodepoints.java @@ -0,0 +1,41 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +final class UsefulCodepoints { + + static final int QUOTATION_MARK = 0x22; + static final int PERCENT_SIGN = 0x25; + static final int LEFT_PARENTHESIS = 0x28; + static final int RIGHT_PARENTHESIS = 0x29; + static final int ASTERISK = 0x2A; + static final int HYPHEN_MINUS = 0x2D; + static final int SOLIDUS = 0x2F; + static final int SEMICOLON = 0x3B; + static final int EQUALS_SIGN = 0x3D; + static final int LEFT_SQUARE_BRACKET = 0x5B; + static final int RIGHT_SQUARE_BRACKET = 0x5D; + + private UsefulCodepoints() {} +} diff --git a/vocabulary-format-assertion/src/main/java/module-info.java b/vocabulary-format-assertion/src/main/java/module-info.java new file mode 100644 index 00000000..a536ada1 --- /dev/null +++ b/vocabulary-format-assertion/src/main/java/module-info.java @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { + requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; + requires io.github.sebastiantoepfer.jsonschema; + requires io.github.sebastiantoepfer.ddd.common; + requires java.logging; + requires jakarta.json; + + provides io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies + with io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.FormatAssertionVocabulary; +} diff --git a/vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies b/vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies new file mode 100644 index 00000000..809d22a5 --- /dev/null +++ b/vocabulary-format-assertion/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies @@ -0,0 +1 @@ +io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.FormatAssertionVocabulary diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java new file mode 100644 index 00000000..088271a0 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/DefaultFormatAssertionTest.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +class DefaultFormatAssertionTest { + + @Test + void should_be_valid_if_predicate_match() { + assertThat(new DefaultFormatAssertion("test", s -> s.equals("test")).isValidFor("test"), is(true)); + } + + @Test + void should_be_invalid_if_predicate_not_match() { + assertThat(new DefaultFormatAssertion("test", s -> s.equals("test")).isValidFor("hallo"), is(false)); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.java new file mode 100644 index 00000000..f316b82c --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionKeywordTypeTest.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; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.jsonschema.JsonSchema; +import io.github.sebastiantoepfer.jsonschema.JsonSchemas; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import java.util.List; +import org.junit.jupiter.api.Test; + +class FormatAssertionKeywordTypeTest { + + @Test + void should_know_his_name() { + final Keyword keyword = new FormatAssertionKeywordType(List.of(new TestFormat())) + .createKeyword(JsonSchemas.load(Json.createObjectBuilder().add("format", "date").build())); + + assertThat(keyword.hasName("format"), is(true)); + assertThat(keyword.hasName("test"), is(false)); + } + + @Test + void should_not_be_createable_with_non_string() { + final JsonSchema schema = JsonSchemas.load(JsonValue.TRUE); + final KeywordType keywordType = new FormatAssertionKeywordType(List.of(new TestFormat())); + assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(schema)); + } + + @Test + void should_not_be_createable_with_unknown_formatname() { + final JsonSchema schema = JsonSchemas.load(Json.createObjectBuilder().add("format", "test").build()); + final KeywordType keywordType = new FormatAssertionKeywordType(List.of(new TestFormat())); + assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(schema)); + } + + @Test + void should_be_assertion_and_annotation() { + assertThat( + new FormatAssertionKeywordType(List.of(new TestFormat())) + .createKeyword(JsonSchemas.load(Json.createObjectBuilder().add("format", "date").build())) + .categories(), + containsInAnyOrder(Keyword.KeywordCategory.ASSERTION, Keyword.KeywordCategory.ANNOTATION) + ); + } + + @Test + void should_return_formatname_as_annotation() { + assertThat( + new FormatAssertionKeywordType(List.of(new TestFormat())) + .createKeyword(JsonSchemas.load(Json.createObjectBuilder().add("format", "date").build())) + .asAnnotation() + .valueFor(JsonValue.EMPTY_JSON_OBJECT), + is(Json.createValue("date")) + ); + } + + private static final class TestFormat implements FormatAssertion { + + private final String name; + + public TestFormat() { + this("date"); + } + + public TestFormat(final String name) { + this.name = name; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isValidFor(final String value) { + return true; + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java new file mode 100644 index 00000000..1e6722a5 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/FormatAssertionVocabularyTest.java @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion; + +import static com.github.npathai.hamcrestopt.OptionalMatchers.isEmpty; +import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent; +import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresentAnd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.JsonSchemas; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.Json; +import java.net.URI; +import org.junit.jupiter.api.Test; + +class FormatAssertionVocabularyTest { + + @Test + void should_be_return_empty_for_wrong_vocab_id() { + assertThat(new FormatAssertionVocabulary().loadVocabularyWithId(URI.create("http://localhost")), isEmpty()); + } + + @Test + void should_be_return_vocab_for_right_vocab_id() { + assertThat( + new FormatAssertionVocabulary() + .loadVocabularyWithId(URI.create("https://json-schema.org/draft/2020-12/vocab/format-assertion")), + isPresent() + ); + } + + @Test + void should_be_loaded_by_lazy_vocab_and_keyword_format_should_be_an_assertion_results_in_false() { + assertThat( + JsonSchemas + .load( + Json + .createObjectBuilder() + .add( + "$vocabulary", + Json + .createObjectBuilder() + .add("https://json-schema.org/draft/2020-12/vocab/format-assertion", true) + ) + .add("format", "email") + .build() + ) + .keywordByName("format") + .map(Keyword::asAssertion) + .map(assertion -> assertion.isValidFor(Json.createValue("test"))), + isPresentAnd(is(false)) + ); + } + + @Test + void should_be_loaded_by_lazy_vocab_and_keyword_format_should_be_an_assertion_results_in_true() { + assertThat( + JsonSchemas + .load( + Json + .createObjectBuilder() + .add( + "$vocabulary", + Json + .createObjectBuilder() + .add("https://json-schema.org/draft/2020-12/vocab/format-assertion", true) + ) + .add("format", "email") + .build() + ) + .keywordByName("format") + .map(Keyword::asAssertion) + .map(assertion -> + assertion.isValidFor(Json.createValue("61313468+sebastian-toepfer@users.noreply.github.com")) + ), + isPresentAnd(is(true)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java new file mode 100644 index 00000000..829de2f8 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/CoreRulesTest.java @@ -0,0 +1,425 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValidateableCodePoint; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class CoreRulesTest { + + @Test + void should_be_printable() { + assertThat( + CoreRules.VCHAR.printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("corerule")), + hasEntry(is("name"), allOf(hasEntry(is("name"), is("VCHAR")), hasEntry(is("type"), is("rulename")))), + hasEntry( + is("elements"), + allOf( + hasEntry(is("type"), is("val-range")), + hasEntry( + is("from"), + allOf(hasEntry(is("base"), is("HEXADECIMAL")), (Matcher) hasEntry(is("value"), is(0x21))) + ), + hasEntry( + is("to"), + allOf(hasEntry(is("base"), is("HEXADECIMAL")), (Matcher) hasEntry(is("value"), is(0x7E))) + ) + ) + ) + ) + ); + } + + @Nested + class Alpha { + + @Test + void should_return_rulename() { + assertThat(CoreRules.ALPHA.asRuleName(), is(RuleName.of("ALPHA"))); + } + + @ParameterizedTest(name = "{0} is a valid alpha") + @ValueSource(ints = { 0x41, 0x51, 0x5A, 0x61, 0x71, 0x7A }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.ALPHA.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid alpha") + @ValueSource(ints = { 0x30, 0x39, 0x5B, 0x60, 0x7B }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.ALPHA.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Bit { + + @Test + void should_be_valid_for_codepoint_of_zero() { + assertThat(CoreRules.BIT.isValidFor(0x30), is(true)); + } + + @Test + void should_be_valid_for_codepoint_of_one() { + assertThat(CoreRules.BIT.isValidFor(0x31), is(true)); + } + + @Test + void should_be_invalid_for_codepoint_of_two() { + assertThat(CoreRules.BIT.isValidFor(0x32), is(false)); + } + } + + @Nested + class Char { + + @ParameterizedTest(name = "{0} is a valid char") + @ValueSource(ints = { 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x7F }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.CHAR.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid char") + @ValueSource(ints = { 0x00, 0x80, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0xFF }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.CHAR.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Cr { + + @Test + void should_be_valid_for_cr() { + assertThat(CoreRules.CR.isValidFor(0x0D), is(true)); + } + + @Test + void should_be_invalid_for_whitespace() { + assertThat(CoreRules.CR.isValidFor(0x00), is(false)); + } + + @Test + void should_be_invalid_for_linefeed() { + assertThat(CoreRules.CR.isValidFor(0x0A), is(false)); + } + + @Test + void should_be_invalid_for_a() { + assertThat(CoreRules.CR.isValidFor(0x41), is(false)); + } + } + + @Nested + class Crlf { + + @Test + void should_return_rulename() { + assertThat(CoreRules.CRLF.asRuleName(), is(RuleName.of("CRLF"))); + } + + @Test + void should_be_valid_for_crlf() { + final Element element = CoreRules.CRLF; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, '\r')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, '\n')), is(true)); + } + } + + @Nested + class Ctl { + + @Test + void should_return_rulename() { + assertThat(CoreRules.CTL.asRuleName(), is(RuleName.of("CTL"))); + } + + @ParameterizedTest(name = "{0} is a valid CTL") + @ValueSource(ints = { 0x00, 0x01, 0x11, 0x1A, 0x1F, 0x7F }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.CTL.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid CTL") + @ValueSource(ints = { 0x20, 0x30, 0x6A, 0x7A, 0x7E }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.CTL.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Digit { + + @Test + void should_return_rulename() { + assertThat(CoreRules.DIGIT.asRuleName(), is(RuleName.of("DIGIT"))); + } + + @ParameterizedTest(name = "{0} is a valid digit") + @ValueSource(ints = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.DIGIT.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid dig") + @ValueSource(ints = { 0x00, 0x10, 0x21, 0x29, 0x40, 0x50, 0x60 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.DIGIT.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Dquote { + + @Test + void should_be_valid() { + assertThat(CoreRules.DQUOTE.isValidFor(0x22), is(true)); + } + + @Test + void should_be_invalid() { + assertThat(CoreRules.DQUOTE.isValidFor(0x21), is(false)); + } + } + + @Nested + class Hexdig { + + @ParameterizedTest(name = "{0} is a valid hexdig") + @ValueSource( + ints = { + 0x30, + 0x31, + 0x32, + 0x33, + 0x34, + 0x35, + 0x36, + 0x37, + 0x38, + 0x39, + 0x41, + 0x42, + 0x43, + 0x44, + 0x45, + 0x46, + 0x61, + 0x62, + 0x64, + 0x65, + 0x66, + } + ) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.HEXDIG.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid hexdig") + @ValueSource(ints = { 0x00, 0x10, 0x21, 0x29, 0x47, 0x50, 0x60, 0x67, 0x71 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.HEXDIG.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Htab { + + @Test + void should_be_valid_for_htab() { + assertThat(CoreRules.HTAB.isValidFor(0x09), is(true)); + } + + @Test + void should_be_invalid_for_whitespace() { + assertThat(CoreRules.HTAB.isValidFor(0x00), is(false)); + } + + @Test + void should_be_invalid_for_linefeed() { + assertThat(CoreRules.HTAB.isValidFor(0x0A), is(false)); + } + + @Test + void should_be_invalid_for_a() { + assertThat(CoreRules.HTAB.isValidFor(0x41), is(false)); + } + } + + @Nested + class Lf { + + @Test + void should_return_rulename() { + assertThat(CoreRules.LF.asRuleName(), is(RuleName.of("LF"))); + } + + @Test + void should_be_valid_for_linefeed() { + assertThat(CoreRules.LF.isValidFor(0x0A), is(true)); + } + + @Test + void should_be_invalid_for_whitespace() { + assertThat(CoreRules.LF.isValidFor(0x00), is(false)); + } + + @Test + void should_be_invalid_for_cr() { + assertThat(CoreRules.LF.isValidFor(0x0D), is(false)); + } + + @Test + void should_be_invalid_for_a() { + assertThat(CoreRules.LF.isValidFor(0x41), is(false)); + } + } + + @Nested + class Lwsp { + + @Test + void should_return_rulename() { + assertThat(CoreRules.LWSP.asRuleName(), is(RuleName.of("LWSP"))); + } + + @Test + void should_be_valid_for_sp_sp() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 0x20)), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 0x20)), is(true)); + } + + @Test + void should_be_valid_for_sp_htab() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 0x20)), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 0x09)), is(true)); + } + + @Test + void should_be_valid_for_htab_sp() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 0x09)), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 0x20)), is(true)); + } + + @Test + void should_be_valid_for_crlf_sp() { + final Element element = CoreRules.LWSP; + + assertThat(element.isValidFor(ValidateableCodePoint.of(0, '\r')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, '\n')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(2, 0x20)), is(true)); + } + } + + @Nested + class Octet { + + @ParameterizedTest(name = "{0} is a valid octet") + @ValueSource(ints = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xA1, 0xFF }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.OCTET.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid octet") + @ValueSource(ints = { 0x100, 0x101 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.OCTET.isValidFor(codePoint), is(false)); + } + } + + @Nested + class Space { + + @Test + void should_be_valid_for_space() { + assertThat(CoreRules.SP.isValidFor(0x20), is(true)); + } + + @Test + void should_be_invalid_for_tab() { + assertThat(CoreRules.SP.isValidFor(0x09), is(false)); + } + + @Test + void should_be_invalid_for_linefeed() { + assertThat(CoreRules.SP.isValidFor(0x0A), is(false)); + } + } + + @Nested + class VisibleCharacters { + + @ParameterizedTest(name = "{0} is a valid vchat") + @ValueSource(ints = { 0x21, 0x31, 0x42, 0x55, 0x65, 0x71, 0x7E }) + void should_be_valid_for(final int codePoint) { + assertThat(CoreRules.VCHAR.isValidFor(codePoint), is(true)); + } + + @ParameterizedTest(name = "{0} is not a valid dig") + @ValueSource(ints = { 0x00, 0x10, 0x20, 0x7F, 0x80 }) + void should_be_invalid_for(final int codePoint) { + assertThat(CoreRules.VCHAR.isValidFor(codePoint), is(false)); + } + } + + @Nested + class WhiteSpace { + + @Test + void should_be_valid_for_space() { + assertThat(CoreRules.WSP.isValidFor(0x20), is(true)); + } + + @Test + void should_be_valid_for_htab() { + assertThat(CoreRules.WSP.isValidFor(0x09), is(true)); + } + + @Test + void should_be_valid_for_linefeed() { + assertThat(CoreRules.WSP.isValidFor(0x0A), is(false)); + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.java new file mode 100644 index 00000000..90cf552d --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleListTest.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; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class RuleListTest { + + @Test + void should_create_new() { + assertThat(RuleList.of(Rule.of(RuleName.of("test"), StringElement.of("test"))), is(not(nullValue()))); + } + + @Test + void equalsContract() { + EqualsVerifier.forClass(RuleList.class).verify(); + } + + @Test + void should_be_printable() { + assertThat( + RuleList + .of(Rule.of(RuleName.of("rulename"), Alternative.of(StringElement.of("/"), StringElement.of(";")))) + .printOn(new HashMapMedia()), + (Matcher) hasEntry( + is("rules"), + contains( + allOf( + (Matcher) hasEntry(is("type"), is("rule")), + hasEntry( + is("name"), + allOf(hasEntry(is("name"), is("rulename")), hasEntry(is("type"), is("rulename"))) + ), + hasEntry( + is("elements"), + allOf( + (Matcher) hasEntry(is("type"), is("alternative")), + hasEntry( + is("alternatives"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ) + ) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java new file mode 100644 index 00000000..9494d5c9 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/RuleTest.java @@ -0,0 +1,131 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class RuleTest { + + @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)); + } + + @Test + void should_be_printable() { + assertThat( + Rule + .of(RuleName.of("rulename"), Alternative.of(StringElement.of("/"), StringElement.of(";"))) + .printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("rule")), + hasEntry(is("name"), allOf(hasEntry(is("name"), is("rulename")), hasEntry(is("type"), is("rulename")))), + hasEntry( + is("elements"), + allOf( + (Matcher) hasEntry(is("type"), is("alternative")), + hasEntry( + is("alternatives"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ) + ) + ); + } + + @Nested + class Validation { + + private Rule ruleName; + + @BeforeEach + void initRule() { + ruleName = + Rule.of( + RuleName.of("rulename"), + Concatenation.of( + CoreRules.ALPHA, + VariableRepetition.of( + SequenceGroup.of(Alternative.of(CoreRules.ALPHA, CoreRules.DIGIT, StringElement.of("-"))) + ) + ) + ); + } + + @Test + void should_be_valid_for_alphas_only() { + assertThat(ruleName.asPredicate().test("rulename"), is(true)); + } + + @Test + void should_be_valid_for_valid_alpha_and_digits() { + assertThat(ruleName.asPredicate().test("rul3nam3"), is(true)); + } + + @Test + void should_be_valid_for_valid_alpha_and_minus() { + assertThat(ruleName.asPredicate().test("rule-name"), is(true)); + } + + @Test + void should_be_invalid_for_value_with_digist_at_start() { + assertThat(ruleName.asPredicate().test("1rule"), is(false)); + } + + @Test + void should_be_invalid_for_value_with_solidus() { + assertThat(ruleName.asPredicate().test("rule/name"), is(false)); + } + } +} 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..12cea960 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/AlternativeTest.java @@ -0,0 +1,43 @@ +/* + * 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 AlternativeTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(Alternative.class).verify(); + } + + @Test + void should_return_dimension_from_smallest_and_larges() { + assertThat(Alternative.of(StringElement.of("ab"), StringElement.of("cde")).dimension(), is(Dimension.of(2, 3))); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java new file mode 100644 index 00000000..f6911ea8 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ConcatenationTest.java @@ -0,0 +1,105 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class ConcatenationTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(Concatenation.class).verify(); + } + + @Test + void should_return_dimension_as_sum() { + assertThat( + Concatenation + .of( + Alternative.of(StringElement.of("ab"), StringElement.of("cde")), + Alternative.of(StringElement.of("x"), StringElement.of("yz")) + ) + .dimension(), + is(Dimension.of(3, 5)) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position() { + final Concatenation concatenation = Concatenation.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(0, 'a')), is(true)); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(1, 'b')), is(true)); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(2, 'c')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + final Concatenation concatenation = Concatenation.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(3, 'c')), is(false)); + } + + @Test + void should_be_invalid_if_codepoint_is_notequals_codepoint_at_position() { + final Concatenation concatenation = Concatenation.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(concatenation.isValidFor(ValidateableCodePoint.of(1, 'B')), is(false)); + } + + @Test + void should_be_printable() { + assertThat( + Concatenation.of(StringElement.of("/"), StringElement.of(";")).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("concatenation")), + hasEntry( + is("concatenations"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java new file mode 100644 index 00000000..49d13bac --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/DimensionTest.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; + +import java.util.List; +import java.util.TreeSet; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class DimensionTest { + + @Test + void should_equals_contract() { + EqualsVerifier.forClass(Dimension.class).verify(); + } + + @Test + void should_know_if_codepoint_is_in_range() { + assertThat(Dimension.of(1).isInRange(ValidateableCodePoint.of(0, 0x13)), is(true)); + } + + @Test + void should_know_if_codepoint_is_not_in_range() { + assertThat(Dimension.of(1).isInRange(ValidateableCodePoint.of(1, 0x13)), is(false)); + } + + @Test + void should_create_expanded_upper_bondary() { + assertThat(Dimension.of(2).expandTo(Dimension.of(3, 7)), is(Dimension.of(2, 7))); + } + + @Test + void should_create_expanded_lower_and_upper_bondary() { + assertThat(Dimension.of(2).expandTo(Dimension.of(1, 7)), is(Dimension.of(1, 7))); + } + + @Test + void should_create_expanded_lower_bondary() { + assertThat(Dimension.of(2, 8).expandTo(Dimension.of(1, 7)), is(Dimension.of(1, 8))); + } + + @Test + void should_be_sortable() { + assertThat( + new TreeSet<>( + List.of( + Dimension.of(6), + Dimension.of(2, 4), + Dimension.of(2), + Dimension.of(1, 7), + Dimension.of(2, 4), + Dimension.of(2, 6) + ) + ), + contains(Dimension.of(2), Dimension.of(2, 4), Dimension.of(2, 6), Dimension.of(6), Dimension.of(1, 7)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java new file mode 100644 index 00000000..2e335dee --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ElementTest.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.ddd.common.Media; +import org.junit.jupiter.api.Test; + +class ElementTest { + + @Test + void should_had_dimension_of_one_by_default() { + assertThat(new TestElement().dimension(), is(Dimension.of(1))); + } + + private static class TestElement implements Element { + + @Override + public boolean isValidFor(ValidateableCodePoint codePoint) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public > T printOn(T media) { + throw new UnsupportedOperationException("Not supported yet."); + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/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..f82902c1 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/NumericCharacterTest.java @@ -0,0 +1,85 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class NumericCharacterTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(NumericCharacter.class).withOnlyTheseFields("value").verify(); + } + + @Test + void should_parse_int_with_dec_radix() { + assertThat(NumericCharacter.BASE.DECIMAL.convert("11"), is(11)); + } + + @Test + void should_parse_int_with_bin_radix() { + assertThat(NumericCharacter.BASE.BINARY.convert("11"), is(3)); + } + + @Test + void should_create_bin_base() { + assertThat(NumericCharacter.BASE.findByShortName('b'), is(NumericCharacter.BASE.BINARY)); + } + + @Test + void should_create_dec_base() { + assertThat(NumericCharacter.BASE.findByShortName('d'), is(NumericCharacter.BASE.DECIMAL)); + } + + @Test + void should_create_hex_base() { + assertThat(NumericCharacter.BASE.findByShortName('x'), is(NumericCharacter.BASE.HEXADECIMAL)); + } + + @Test + void should_throw_exception_for_invalid_shortname() { + assertThrows(IllegalArgumentException.class, () -> NumericCharacter.BASE.findByShortName('h')); + } + + @Test + void should_be_printable() { + assertThat( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10101).printOn(new HashMapMedia()), + allOf( + hasEntry(is("type"), is("num-val")), + hasEntry(is("base"), is("BINARY")), + (Matcher) hasEntry(is("value"), is(21)) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java new file mode 100644 index 00000000..500abb78 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/OptionalSequenceTest.java @@ -0,0 +1,86 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import java.util.List; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class OptionalSequenceTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(OptionalSequence.class).verify(); + } + + @Test + void should_create_new_optionalsequence() { + assertThat(OptionalSequence.of(StringElement.of(".")), is(not(nullValue()))); + } + + @Test + void should_has_dimension_from_zero_to_max() { + assertThat( + OptionalSequence.of(List.of(StringElement.of("a"), StringElement.of("b"))).dimension(), + is(Dimension.of(0, 2)) + ); + } + + @Test + void should_be_printable() { + assertThat( + OptionalSequence.of(List.of(StringElement.of("/"), StringElement.of(";"))).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("option")), + hasEntry( + is("optionals"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ); + } + + @Test + void should_be_valid_for_every_codepoint() { + assertThat( + OptionalSequence + .of(List.of(StringElement.of("/"), StringElement.of(";"))) + .isValidFor(ValidateableCodePoint.of(0, ',')), + is(true) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java new file mode 100644 index 00000000..11ae7f2f --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleNameTest.java @@ -0,0 +1,35 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class RuleNameTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(RuleName.class).withNonnullFields("name").verify(); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java new file mode 100644 index 00000000..10478e86 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/RuleReferenceTest.java @@ -0,0 +1,89 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class RuleReferenceTest { + + @Test + void should_create_new() { + assertThat(RuleReference.of(RuleName.of("name")), is(not(nullValue()))); + } + + @Test + void equalsContract() { + EqualsVerifier.forClass(RuleReference.class).verify(); + } + + @Test + void should_has_dimension_of_rulename_length() { + assertThat(RuleReference.of(RuleName.of("rule")).dimension(), is(Dimension.of(4))); + } + + @Test + void should_be_printable() { + assertThat( + RuleReference.of(RuleName.of("test")).printOn(new HashMapMedia()), + allOf( + hasEntry(is("type"), is("rule-ref")), + (Matcher) hasEntry( + is("name"), + allOf(hasEntry(is("type"), is("rulename")), hasEntry(is("name"), is("test"))) + ) + ) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position() { + final Element element = RuleReference.of(RuleName.of("test")); + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 't')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 'e')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(2, 's')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(3, 't')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_not_equals_codepoint_at_position() { + final Element element = RuleReference.of(RuleName.of("test")); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 't')), is(false)); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + final Element element = RuleReference.of(RuleName.of("test")); + assertThat(element.isValidFor(ValidateableCodePoint.of(4, 't')), is(false)); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java new file mode 100644 index 00000000..fdcd4ec8 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SequenceGroupTest.java @@ -0,0 +1,119 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class SequenceGroupTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(SequenceGroup.class).verify(); + } + + @Test + void should_create() { + assertThat( + SequenceGroup.of( + StringElement.of("test"), + RuleReference.of(RuleName.of("test")), + SequenceGroup.of(NumericCharacter.of(NumericCharacter.BASE.BINARY, 0)) + ), + is(not(nullValue())) + ); + } + + @Test + void should_return_dimension_as_sum() { + assertThat( + SequenceGroup + .of( + Alternative.of(StringElement.of("ab"), StringElement.of("cde")), + Alternative.of(StringElement.of("x"), StringElement.of("yz")) + ) + .dimension(), + is(Dimension.of(3, 5)) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position() { + final Element element = SequenceGroup.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(element.isValidFor(ValidateableCodePoint.of(0, 'a')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 'b')), is(true)); + assertThat(element.isValidFor(ValidateableCodePoint.of(2, 'c')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + final Element element = SequenceGroup.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(element.isValidFor(ValidateableCodePoint.of(3, 'c')), is(false)); + } + + @Test + void should_be_invalid_if_codepoint_is_notequals_codepoint_at_position() { + final Element element = SequenceGroup.of( + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'b'), + NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'c') + ); + assertThat(element.isValidFor(ValidateableCodePoint.of(1, 'B')), is(false)); + } + + @Test + void should_be_printable() { + assertThat( + SequenceGroup.of(StringElement.of("/"), StringElement.of(";")).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("group")), + hasEntry( + is("elements"), + contains( + allOf(hasEntry("type", "char-val"), hasEntry("value", "/")), + allOf(hasEntry("type", "char-val"), hasEntry("value", ";")) + ) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.java new file mode 100644 index 00000000..3c5f9cfd --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/SpecificRepetitionTest.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 static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class SpecificRepetitionTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(SpecificRepetition.class).verify(); + } + + @Test + void should_create_new_element() { + assertThat(SpecificRepetition.of(StringElement.of("1"), 1), is(not(nullValue()))); + } + + @Test + void should_dimension_as_multimple_of() { + assertThat( + SpecificRepetition.of(Alternative.of(StringElement.of("ab"), StringElement.of("cde")), 2).dimension(), + is(Dimension.of(4, 6)) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position_on_first_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(0, '1')), + is(true) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position_on_any_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(1, '1')), + is(true) + ); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_at_position_on_last_posible_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(2, '1')), + is(true) + ); + } + + @Test + void should_be_invalid_if_codepoint_is_notequals_codepoint_at_position_on_any_posible_repeation() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(1, '2')), + is(false) + ); + } + + @Test + void should_be_invalid_if_codepoint_is_out_of_position() { + assertThat( + SpecificRepetition.of(StringElement.of("1"), 3).isValidFor(ValidateableCodePoint.of(3, '1')), + is(false) + ); + } + + @Test + void should_be_printable() { + assertThat( + SpecificRepetition.of(StringElement.of("."), 2).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("fix-repetition")), + (Matcher) hasEntry(is("repeat"), is(2)), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/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..249ac865 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/StringElementTest.java @@ -0,0 +1,84 @@ +/* + * 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 StringElementTest { + + @Test + void should_create_new() { + assertThat(StringElement.of("test"), is(not(nullValue()))); + } + + @Test + void equalsContract() { + EqualsVerifier.forClass(StringElement.class).withNonnullFields("value").verify(); + } + + @Test + void should_return_string_length_as_dimension() { + assertThat(StringElement.of("abc").dimension(), is(Dimension.of(3))); + } + + @Test + void should_be_invalid_if_codepoint_is_outside_the_dimension() { + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(3, 'a')), is(false)); + } + + @Test + void should_be_valid_if_codepoint_is_equals_codepoint_in_string_at_position() { + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(0, 'a')), is(true)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(1, 'b')), is(true)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(2, 'c')), is(true)); + } + + @Test + void should_be_invalid_if_codepoint_is_not_equals_codepoint_in_string_at_position() { + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(0, 'b')), is(false)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(1, 'a')), is(false)); + assertThat(StringElement.of("abc").isValidFor(ValidateableCodePoint.of(2, 'b')), is(false)); + } + + @Test + void should_be_valid_if_codePoint_is_lowercase_representation() { + assertThat(StringElement.of("A").isValidFor(ValidateableCodePoint.of(0, 0x61)), is(true)); + } + + @Test + void should_be_valid_if_codePoint_is_upercase_representation() { + assertThat(StringElement.of("A").isValidFor(ValidateableCodePoint.of(0, 0x41)), is(true)); + } + + @Test + void should_be_invalid_if_codePoint_is_neither_upercase_nor_lowercase_representation() { + assertThat(StringElement.of("A").isValidFor(ValidateableCodePoint.of(0, 0x42)), is(false)); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java new file mode 100644 index 00000000..cb7d8b90 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValidateableCodePointTest.java @@ -0,0 +1,59 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +class ValidateableCodePointTest { + + @Test + void should_equals_contract() { + EqualsVerifier.forClass(ValidateableCodePoint.class).verify(); + } + + @Test + void should_not_be_creatable_with_position_lower_zero() { + assertThrows(IllegalArgumentException.class, () -> ValidateableCodePoint.of(-1, 0x00)); + } + + @Test + void should_not_be_creatable_with_codepoint_lower_zero() { + assertThrows(IllegalArgumentException.class, () -> ValidateableCodePoint.of(0, -1)); + } + + @Test + void should_return_his_position() { + assertThat(ValidateableCodePoint.of(10, 0x13).position(), is(10)); + } + + @Test + void should_repositionable_by_offset() { + assertThat(ValidateableCodePoint.of(10, 0x13).repositionBackBy(9), is(ValidateableCodePoint.of(1, 0x13))); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java new file mode 100644 index 00000000..1675aabb --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/ValueRangeAlternativesTest.java @@ -0,0 +1,153 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class ValueRangeAlternativesTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(ValueRangeAlternatives.class).verify(); + } + + @Test + void should_create_a_new_value_range() { + assertThat( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0), + NumericCharacter.of(NumericCharacter.BASE.BINARY, 1) + ), + is(not(nullValue())) + ); + } + + @Test + void should_be_invalid_if_value_too_small() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(ValidateableCodePoint.of(0, 0x01)), + is(false) + ); + } + + @Test + void should_be_invalid_if_value_too_big() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(ValidateableCodePoint.of(0, 0x05)), + is(false) + ); + } + + @Test + void should_be_valid_if_value_is_in_range() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(ValidateableCodePoint.of(0, 0x03)), + is(true) + ); + } + + @Test + void should_be_valid_if_value_is_minimum() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(ValidateableCodePoint.of(0, 0x02)), + is(true) + ); + } + + @Test + void should_be_valid_if_value_is_maximum() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x02), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x04) + ) + .isValidFor(ValidateableCodePoint.of(0, 0x04)), + is(true) + ); + } + + @Test + void should_be_printable() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10101), + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10111) + ) + .printOn(new HashMapMedia()), + allOf( + hasEntry(is("type"), is("val-range")), + hasEntry( + is("from"), + allOf(hasEntry(is("base"), is("BINARY")), (Matcher) hasEntry(is("value"), is(21))) + ), + hasEntry(is("to"), allOf(hasEntry(is("base"), is("BINARY")), (Matcher) hasEntry(is("value"), is(23)))) + ) + ); + } + + @Test + void should_return_two_as_dimension() { + assertThat( + ValueRangeAlternatives + .of( + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10101), + NumericCharacter.of(NumericCharacter.BASE.BINARY, 0b10111) + ) + .dimension(), + is(Dimension.of(1)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java new file mode 100644 index 00000000..8745067a --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/element/VariableRepetitionTest.java @@ -0,0 +1,170 @@ +/* + * 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.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class VariableRepetitionTest { + + @Test + void equalsContract() { + EqualsVerifier.forClass(VariableRepetition.class).verify(); + } + + @Test + void should_create_an_exactly() { + assertThat( + VariableRepetition.ofExactly(RuleReference.of(RuleName.of("test")), 2), + is(VariableRepetition.ofBetween(RuleReference.of(RuleName.of("test")), 2, 2)) + ); + } + + @Test + void should_create_new_at_most_element() { + assertThat(VariableRepetition.ofAtMost(StringElement.of("1"), 2), is(not(nullValue()))); + } + + @Test + void should_create_new_at_least_element() { + assertThat(VariableRepetition.ofAtLeast(StringElement.of("1"), 2), is(not(nullValue()))); + } + + @Test + void should_create_new_optional_element() { + assertThat(VariableRepetition.of(StringElement.of("1")), is(not(nullValue()))); + } + + @Test + void should_be_printable() { + assertThat( + VariableRepetition.of(StringElement.of(".")).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + not(hasKey("atLeast")), + not(hasKey("atMost")), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_be_printable_with_atLeast() { + assertThat( + VariableRepetition.ofAtLeast(StringElement.of("."), 2).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + (Matcher) hasEntry(is("atLeast"), is(2)), + not(hasKey("atMost")), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_be_printable_with_atMost() { + assertThat( + VariableRepetition.ofAtMost(StringElement.of("."), 10).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + (Matcher) hasEntry(is("atMost"), is(10)), + not(hasKey("atLeast")), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_be_printable_with_between() { + assertThat( + VariableRepetition.ofBetween(StringElement.of("."), 2, 10).printOn(new HashMapMedia()), + allOf( + (Matcher) hasEntry(is("type"), is("repetition")), + (Matcher) hasEntry(is("atLeast"), is(2)), + (Matcher) hasEntry(is("atMost"), is(10)), + hasEntry(is("element"), allOf(hasEntry(is("type"), is("char-val")), hasEntry(is("value"), is(".")))) + ) + ); + } + + @Test + void should_dimension_as_multimple_of() { + assertThat( + VariableRepetition + .ofBetween(Alternative.of(StringElement.of("ab"), StringElement.of("cde")), 2, 4) + .dimension(), + is(Dimension.of(4, 12)) + ); + } + + @Test + void should_be_valid_if_codepoint_matches_codepoint_at_posion_of_first_repeation() { + assertThat( + VariableRepetition + .ofAtLeast(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(0, 'a')), + is(true) + ); + } + + @Test + void should_be_valid_if_codepoint_matches_codepoint_at_posion_of_any_repeation() { + assertThat( + VariableRepetition + .ofAtLeast(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(4, 'a')), + is(true) + ); + } + + @Test + void should_be_invalid_if_codepoint_not_equals_at_posion_for_min_repeation() { + assertThat( + VariableRepetition + .ofAtLeast(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(1, 'b')), + is(false) + ); + } + + @Test + void should_be_invalod_if_codepoint_out_of_posion() { + assertThat( + VariableRepetition + .ofAtMost(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 'a'), 2) + .isValidFor(ValidateableCodePoint.of(3, 'a')), + is(false) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java new file mode 100644 index 00000000..ebf254d8 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ABNFsTest.java @@ -0,0 +1,393 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.CoreRules; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import java.nio.charset.StandardCharsets; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +class ABNFsTest { + + @Test + void should_create_abnf_rules_from_string() { + //Note: a rule MUST end with crlf!! see rfc5234. lf is already set by the formatting :) + assertThat( + ABNFs + .of( + """ + rulelist = 1*( rule / (*c-wsp c-nl) )\r + rule = rulename defined-as elements c-nl\r + rulename = ALPHA *(ALPHA / DIGIT / "-")\r + defined-as = *c-wsp ("=" / "=/") *c-wsp\r + elements = alternation *c-wsp\r + c-wsp = WSP / (c-nl WSP)\r + c-nl = comment / CRLF\r + comment = ";" *(WSP / VCHAR) CRLF\r + alternation = concatenation\r + *(*c-wsp "/" *c-wsp concatenation)\r + concatenation = repetition *(1*c-wsp repetition)\r + repetition = [repeat] element\r + repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)\r + element = rulename / group / option /\r + char-val / num-val / prose-val\r + group = "(" *c-wsp alternation *c-wsp ")"\r + option = "[" *c-wsp alternation *c-wsp "]"\r + char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE\r + num-val = "%" (bin-val / dec-val / hex-val)\r + bin-val = "b" 1*BIT\r + [ 1*("." 1*BIT) / ("-" 1*BIT) ]\r + dec-val = "d" 1*DIGIT\r + [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]\r + hex-val = "x" 1*HEXDIG\r + [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]\r + """ + ) + .rules(), + isAbnfSpecAsRuleList() + ); + } + + @Test + void should_create_abnf_rules_from_inputstream() { + assertThat( + ABNFs.of(ABNFs.class.getClassLoader().getResourceAsStream("ABNFspec.txt"), StandardCharsets.UTF_8).rules(), + isAbnfSpecAsRuleList() + ); + } + + private static Matcher isAbnfSpecAsRuleList() { + return is( + RuleList.of( + Rule.of( + RuleName.of("rulelist"), + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Alternative.of( + RuleReference.of(RuleName.of("rule")), + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("c-nl")) + ) + ) + ) + ), + 1 + ) + ), + Rule.of( + RuleName.of("rule"), + Concatenation.of( + RuleReference.of(RuleName.of("rulename")), + RuleReference.of(RuleName.of("defined-as")), + RuleReference.of(RuleName.of("elements")), + RuleReference.of(RuleName.of("c-nl")) + ) + ), + Rule.of( + RuleName.of("rulename"), + Concatenation.of( + RuleReference.of(CoreRules.ALPHA.asRuleName()), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + RuleReference.of(CoreRules.ALPHA.asRuleName()), + RuleReference.of(CoreRules.DIGIT.asRuleName()), + StringElement.of("-") + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("defined-as"), + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + SequenceGroup.of(Alternative.of(StringElement.of("="), StringElement.of("=/"))), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) + ) + ), + Rule.of( + RuleName.of("elements"), + Concatenation.of( + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))) + ) + ), + Rule.of( + RuleName.of("c-wsp"), + Alternative.of( + RuleReference.of(RuleName.of("WSP")), + SequenceGroup.of( + Concatenation.of( + RuleReference.of(RuleName.of("c-nl")), + RuleReference.of(CoreRules.WSP.asRuleName()) + ) + ) + ) + ), + Rule.of( + RuleName.of("c-nl"), + Alternative.of( + RuleReference.of(RuleName.of("comment")), + RuleReference.of(CoreRules.CRLF.asRuleName()) + ) + ), + Rule.of( + RuleName.of("comment"), + Concatenation.of( + StringElement.of(";"), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + RuleReference.of(CoreRules.WSP.asRuleName()), + RuleReference.of(CoreRules.VCHAR.asRuleName()) + ) + ) + ), + RuleReference.of(CoreRules.CRLF.asRuleName()) + ) + ), + Rule.of( + RuleName.of("alternation"), + Concatenation.of( + RuleReference.of(RuleName.of("concatenation")), + VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of("/"), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("concatenation")) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("concatenation"), + Concatenation.of( + RuleReference.of(RuleName.of("repetition")), + VariableRepetition.of( + SequenceGroup.of( + Concatenation.of( + VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("c-wsp")), 1), + RuleReference.of(RuleName.of("repetition")) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("repetition"), + Concatenation.of( + OptionalSequence.of(RuleReference.of(RuleName.of("repeat"))), + RuleReference.of(RuleName.of("element")) + ) + ), + Rule.of( + RuleName.of("repeat"), + Alternative.of( + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), + SequenceGroup.of( + Concatenation.of( + VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())), + StringElement.of("*"), + VariableRepetition.of(RuleReference.of(CoreRules.DIGIT.asRuleName())) + ) + ) + ) + ), + Rule.of( + RuleName.of("element"), + Alternative.of( + RuleReference.of(RuleName.of("rulename")), + RuleReference.of(RuleName.of("group")), + RuleReference.of(RuleName.of("option")), + RuleReference.of(RuleName.of("char-val")), + RuleReference.of(RuleName.of("num-val")), + RuleReference.of(RuleName.of("prose-val")) + ) + ), + Rule.of( + RuleName.of("group"), + Concatenation.of( + StringElement.of("("), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of(")") + ) + ), + Rule.of( + RuleName.of("option"), + Concatenation.of( + StringElement.of("["), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + RuleReference.of(RuleName.of("alternation")), + VariableRepetition.of(RuleReference.of(RuleName.of("c-wsp"))), + StringElement.of("]") + ) + ), + Rule.of( + RuleName.of("char-val"), + Concatenation.of( + RuleReference.of(CoreRules.DQUOTE.asRuleName()), + VariableRepetition.of( + SequenceGroup.of( + Alternative.of( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x20), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x21) + ), + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x23), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x7E) + ) + ) + ) + ), + RuleReference.of(CoreRules.DQUOTE.asRuleName()) + ) + ), + Rule.of( + RuleName.of("num-val"), + Concatenation.of( + StringElement.of("%"), + SequenceGroup.of( + Alternative.of( + RuleReference.of(RuleName.of("bin-val")), + RuleReference.of(RuleName.of("dec-val")), + RuleReference.of(RuleName.of("hex-val")) + ) + ) + ) + ), + Rule.of( + RuleName.of("bin-val"), + Concatenation.of( + StringElement.of("b"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.BIT.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.BIT.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.BIT.asRuleName()), 1) + ) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("dec-val"), + Concatenation.of( + StringElement.of("d"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.DIGIT.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.DIGIT.asRuleName()), 1) + ) + ) + ) + ) + ) + ), + Rule.of( + RuleName.of("hex-val"), + Concatenation.of( + StringElement.of("x"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.HEXDIG.asRuleName()), 1), + OptionalSequence.of( + Alternative.of( + VariableRepetition.ofAtLeast( + SequenceGroup.of( + Concatenation.of( + StringElement.of("."), + VariableRepetition.ofAtLeast( + RuleReference.of(CoreRules.HEXDIG.asRuleName()), + 1 + ) + ) + ), + 1 + ), + SequenceGroup.of( + Concatenation.of( + StringElement.of("-"), + VariableRepetition.ofAtLeast(RuleReference.of(CoreRules.HEXDIG.asRuleName()), 1) + ) + ) + ) + ) + ) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java new file mode 100644 index 00000000..61d2cd2e --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ConcatenationExtractorTest.java @@ -0,0 +1,124 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class ConcatenationExtractorTest { + + @Test + void should_create_rulereference() { + assertThat( + ConcatenationExtractor.of(new FakeOwner()).append('a').append(' ').finish().createAs(Element.class), + is(RuleReference.of(RuleName.of("a"))) + ); + } + + @Test + void should_create_repeating_rulereference() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('1') + .append('*') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("a")), 1)) + ); + } + + @Test + void should_create_elements_from_multiline() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('a') + .append('\r') + .append('\n') + .append(' ') + .append('b') + .append(' ') + .finish() + .createAs(Element.class), + is(Concatenation.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b")))) + ); + } + + @Test + void should_create_elements_as_alternative() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('a') + .append('/') + .append('b') + .append(' ') + .finish() + .createAs(Element.class), + is(Alternative.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b")))) + ); + } + + @Test + void should_create_elements_as_mixed_with_alternative() { + assertThat( + ConcatenationExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('b') + .append('/') + .append('c') + .append('/') + .append('d') + .append(' ') + .append('e') + .append(' ') + .finish() + .createAs(Element.class), + is( + Concatenation.of( + RuleReference.of(RuleName.of("a")), + Alternative.of( + RuleReference.of(RuleName.of("b")), + RuleReference.of(RuleName.of("c")), + RuleReference.of(RuleName.of("d")) + ), + RuleReference.of(RuleName.of("e")) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java new file mode 100644 index 00000000..2c58d45a --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/ElementExtractorTest.java @@ -0,0 +1,88 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.StringElement; +import org.junit.jupiter.api.Test; + +class ElementExtractorTest { + + @Test + void should_create_rulereference() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('a').finish().createAs(Element.class), + is(RuleReference.of(RuleName.of("a"))) + ); + } + + @Test + void should_create_group() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('(').append('a').append(')').finish().createAs(Element.class), + is(SequenceGroup.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_option() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('[').append('a').append(']').finish().createAs(Element.class), + is(OptionalSequence.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_char_val() { + assertThat( + ElementExtractor.of(new FakeOwner()).append('"').append('a').append('"').finish().createAs(Element.class), + is(StringElement.of("a")) + ); + } + + @Test + void should_num_val() { + assertThat( + ElementExtractor + .of(new FakeOwner()) + .append('%') + .append('b') + .append('1') + .append('0') + .append('1') + .append('0') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.BINARY, 10)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java new file mode 100644 index 00000000..b052a2de --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/FakeOwner.java @@ -0,0 +1,57 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +final class FakeOwner implements Extractor, ExtractorOwner, Creator { + + private final Creator creator; + + FakeOwner() { + this(null); + } + + private FakeOwner(final Creator creator) { + this.creator = creator; + } + + @Override + public Extractor append(final int codePoint) { + return this; + } + + @Override + public Extractor imDone(final Creator creator) { + return new FakeOwner(creator); + } + + @Override + public Creator finish() { + return this; + } + + @Override + public T createAs(final Class cls) { + return creator.createAs(cls); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java new file mode 100644 index 00000000..853d758a --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/GroupExtractorTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Alternative; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SequenceGroup; +import org.junit.jupiter.api.Test; + +class GroupExtractorTest { + + @Test + void should_create_group_with_one_element() { + assertThat( + GroupExtractor.of(new FakeOwner()).append('a').append(')').finish().createAs(Element.class), + is(SequenceGroup.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_group_with_more_than_one_element() { + assertThat( + GroupExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('b') + .append(')') + .finish() + .createAs(Element.class), + is( + SequenceGroup.of( + Concatenation.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b"))) + ) + ) + ); + } + + @Test + void should_create_alternative_inside() { + assertThat( + GroupExtractor + .of(new FakeOwner()) + .append('a') + .append('/') + .append('b') + .append(')') + .finish() + .createAs(Element.class), + is(SequenceGroup.of(Alternative.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b"))))) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java new file mode 100644 index 00000000..281426b6 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NewLineDetectorTest.java @@ -0,0 +1,107 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +class NewLineDetectorTest { + + @Test + void should_not_reach_end_after_cr() { + assertThat(new NewLineDetector().append('\r').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlf() { + assertThat(new NewLineDetector().append('\r').append('\n').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlfwsp() { + assertThat(new NewLineDetector().append('\r').append('\n').append(' ').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlflf() { + assertThat(new NewLineDetector().append('\r').append('\n').append('\n').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crnonwsp() { + assertThat(new NewLineDetector().append('\r').append('a').append('a').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_lfnonwsp() { + assertThat(new NewLineDetector().append('\n').append('a').isEndReached(), is(false)); + } + + @Test + void should_not_reach_end_after_crlfwspnonwsp() { + assertThat(new NewLineDetector().append('\r').append('\n').append(' ').append('a').isEndReached(), is(false)); + } + + @Test + void should_reach_end_after_crlfnonwsp() { + assertThat(new NewLineDetector().append('\r').append('\n').append('a').isEndReached(), is(true)); + } + + @Test + void should_apply_codepoints() { + assertThat( + new NewLineDetector() + .append('\r') + .append('\n') + .append('a') + .applyTo(new StringExtractor()) + .finish() + .createAs(String.class), + is("\r\na") + ); + } + + private static class StringExtractor implements Extractor { + + private final StringBuilder sb = new StringBuilder(); + + @Override + public Extractor append(final int codePoint) { + sb.appendCodePoint(codePoint); + return this; + } + + @Override + public Creator finish() { + return new Creator() { + @Override + public T createAs(final Class cls) { + return cls.cast(sb.toString()); + } + }; + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java new file mode 100644 index 00000000..00dc4634 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/NumValExtractorTest.java @@ -0,0 +1,104 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.NumericCharacter; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.ValueRangeAlternatives; +import org.junit.jupiter.api.Test; + +class NumValExtractorTest { + + @Test + void should_create_bin_val() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('b') + .append('0') + .append('1') + .append('0') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.BINARY, 2)) + ); + } + + @Test + void should_create_dec_val() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('d') + .append('3') + .append('1') + .append('0') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.DECIMAL, 310)) + ); + } + + @Test + void should_create_hex_val() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('x') + .append('3') + .append('a') + .append('A') + .finish() + .createAs(Element.class), + is(NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x3AA)) + ); + } + + @Test + void should_create_value_range() { + assertThat( + NumValExtractor + .of(new FakeOwner()) + .append('x') + .append('3') + .append('a') + .append('A') + .append('-') + .append('4') + .append('0') + .append('F') + .finish() + .createAs(Element.class), + is( + ValueRangeAlternatives.of( + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x3AA), + NumericCharacter.of(NumericCharacter.BASE.HEXADECIMAL, 0x40F) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java new file mode 100644 index 00000000..40ed05fe --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/OptionExtractorTest.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Concatenation; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.OptionalSequence; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import org.junit.jupiter.api.Test; + +class OptionExtractorTest { + + @Test + void should_create_option() { + assertThat( + OptionExtractor.of(new FakeOwner()).append('a').append(']').finish().createAs(Element.class), + is(OptionalSequence.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_option_with_multivalues() { + assertThat( + OptionExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('b') + .append(']') + .finish() + .createAs(Element.class), + is( + OptionalSequence.of( + Concatenation.of(RuleReference.of(RuleName.of("a")), RuleReference.of(RuleName.of("b"))) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java new file mode 100644 index 00000000..603c49e5 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RepetitionExtractorTest.java @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.Element; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.SpecificRepetition; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class RepetitionExtractorTest { + + @Test + void should_create_non_repeating_rulereference() { + assertThat( + RepetitionExtractor.of(new FakeOwner()).append('a').append(' ').finish().createAs(Element.class), + is(RuleReference.of(RuleName.of("a"))) + ); + } + + @Test + void should_create_atLeast_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('1') + .append('*') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofAtLeast(RuleReference.of(RuleName.of("a")), 1)) + ); + } + + @Test + void should_create_specificrepetition_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('1') + .append('1') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(SpecificRepetition.of(RuleReference.of(RuleName.of("a")), 11)) + ); + } + + @Test + void should_create_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('*') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.of(RuleReference.of(RuleName.of("a")))) + ); + } + + @Test + void should_create_atMost_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('*') + .append('1') + .append('5') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofAtMost(RuleReference.of(RuleName.of("a")), 15)) + ); + } + + @Test + void should_create_between_repeating_rulereference() { + assertThat( + RepetitionExtractor + .of(new FakeOwner()) + .append('1') + .append('*') + .append('2') + .append('5') + .append('a') + .append(' ') + .finish() + .createAs(Element.class), + is(VariableRepetition.ofBetween(RuleReference.of(RuleName.of("a")), 1, 25)) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java new file mode 100644 index 00000000..95b4541e --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleExtractorTest.java @@ -0,0 +1,73 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.VariableRepetition; +import org.junit.jupiter.api.Test; + +class RuleExtractorTest { + + @Test + void should_create_rule_with_rulereference() { + assertThat( + RuleExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('=') + .append(' ') + .append('b') + .append('\r') + .append('\n') + .finish() + .createAs(Rule.class), + is(Rule.of(RuleName.of("a"), RuleReference.of(RuleName.of("b")))) + ); + } + + @Test + void should_create_rule_with_repatiting_rulereference() { + assertThat( + RuleExtractor + .of(new FakeOwner()) + .append('a') + .append(' ') + .append('=') + .append(' ') + .append('*') + .append('b') + .append('\r') + .append('\n') + .finish() + .createAs(Rule.class), + is(Rule.of(RuleName.of("a"), VariableRepetition.of(RuleReference.of(RuleName.of("b"))))) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java new file mode 100644 index 00000000..51d491e1 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleListExtractorTest.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.Rule; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.RuleList; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleReference; +import org.junit.jupiter.api.Test; + +class RuleListExtractorTest { + + @Test + void should_create_list_with_one_rule_with_rulereference() { + assertThat( + RuleListExtractor + .of() + .append('a') + .append('=') + .append('b') + .append('\r') + .append('\n') + .finish() + .createAs(RuleList.class), + is(RuleList.of(Rule.of(RuleName.of("a"), RuleReference.of(RuleName.of("b"))))) + ); + } + + @Test + void should_create_list_with_two_rules_with_rulereference() { + assertThat( + RuleListExtractor + .of() + .append('a') + .append('=') + .append('b') + .append('\r') + .append('\n') + .append('c') + .append('=') + .append('d') + .append('\r') + .append('\n') + .finish() + .createAs(RuleList.class), + is( + RuleList.of( + Rule.of(RuleName.of("a"), RuleReference.of(RuleName.of("b"))), + Rule.of(RuleName.of("c"), RuleReference.of(RuleName.of("d"))) + ) + ) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java new file mode 100644 index 00000000..6b13dd32 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/RuleNameExtractorTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.element.RuleName; +import org.junit.jupiter.api.Test; + +class RuleNameExtractorTest { + + @Test + void should_create_rule_name_from_alpha() { + assertThat( + RuleNameExtractor.of(new FakeOwner()).append('a').append('b').finish().createAs(RuleName.class), + is(RuleName.of("ab")) + ); + } + + @Test + void should_throw_illegal_argument_if_first_codepoint_is_not_a_valid_letter() { + final Extractor extractor = RuleNameExtractor.of(new FakeOwner()); + assertThrows(IllegalArgumentException.class, () -> extractor.append('1')); + } + + @Test + void should_create_rule_name_from_alpha_and_digit() { + assertThat( + RuleNameExtractor.of(new FakeOwner()).append('a').append('1').finish().createAs(RuleName.class), + is(RuleName.of("a1")) + ); + } + + @Test + void should_create_rule_name_from_alpha_and_minus() { + assertThat( + RuleNameExtractor.of(new FakeOwner()).append('a').append('-').append('b').finish().createAs(RuleName.class), + is(RuleName.of("a-b")) + ); + } + + @Test + void should_stop_rule_name_after_first_non_valid_char() { + assertThat( + RuleNameExtractor + .of(new FakeOwner()) + .append('a') + .append('-') + .append('b') + .append(' ') + .append('c') + .finish() + .createAs(RuleName.class), + is(RuleName.of("a-b")) + ); + } +} diff --git a/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java new file mode 100644 index 00000000..0c70f651 --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/io/github/sebastiantoepfer/jsonschema/vocabulary/format/assertion/abnf/reader/StreambasedABNFTest.java @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion.abnf.reader; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.io.Reader; +import org.junit.jupiter.api.Test; + +class StreambasedABNFTest { + + @Test + void should_close_underlining_reader() throws Exception { + final ReaderImpl r = new ReaderImpl(); + + StreambasedABNF.of(r).close(); + + assertThat(r.isClosed, is(true)); + } + + private class ReaderImpl extends Reader { + + boolean isClosed = false; + + @Override + public int read(char[] chars, int i, int i1) throws IOException { + return -1; + } + + @Override + public void close() throws IOException { + isClosed = true; + } + } +} diff --git a/vocabulary-format-assertion/src/test/java/module-info.java b/vocabulary-format-assertion/src/test/java/module-info.java new file mode 100644 index 00000000..79fd6c6b --- /dev/null +++ b/vocabulary-format-assertion/src/test/java/module-info.java @@ -0,0 +1,37 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +open module io.github.sebastiantoepfer.jsonschema.vocabulary.format.assertion { + requires io.github.sebastiantoepfer.jsonschema.vocabulary.spi; + requires io.github.sebastiantoepfer.jsonschema; + requires io.github.sebastiantoepfer.ddd.common; + requires java.logging; + requires jakarta.json; + + requires io.github.sebastiantoepfer.ddd.media.core; + requires org.junit.jupiter.api; + requires org.junit.jupiter.params; + requires org.hamcrest; + requires nl.jqno.equalsverifier; +} diff --git a/vocabulary-format-assertion/src/test/resources/ABNFspec.txt b/vocabulary-format-assertion/src/test/resources/ABNFspec.txt new file mode 100644 index 00000000..3a5ffd54 --- /dev/null +++ b/vocabulary-format-assertion/src/test/resources/ABNFspec.txt @@ -0,0 +1,25 @@ +rulelist = 1*( rule / (*c-wsp c-nl) ) +rule = rulename defined-as elements c-nl +rulename = ALPHA *(ALPHA / DIGIT / "-") +defined-as = *c-wsp ("=" / "=/") *c-wsp +elements = alternation *c-wsp +c-wsp = WSP / (c-nl WSP) +c-nl = comment / CRLF +comment = ";" *(WSP / VCHAR) CRLF +alternation = concatenation + *(*c-wsp "/" *c-wsp concatenation) +concatenation = repetition *(1*c-wsp repetition) +repetition = [repeat] element +repeat = 1*DIGIT / (*DIGIT "*" *DIGIT) +element = rulename / group / option / + char-val / num-val / prose-val +group = "(" *c-wsp alternation *c-wsp ")" +option = "[" *c-wsp alternation *c-wsp "]" +char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE +num-val = "%" (bin-val / dec-val / hex-val) +bin-val = "b" 1*BIT + [ 1*("." 1*BIT) / ("-" 1*BIT) ] +dec-val = "d" 1*DIGIT + [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ] +hex-val = "x" 1*HEXDIG + [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]