diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/Type.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/Type.java deleted file mode 100644 index 2fa13a7d..00000000 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/Type.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * The MIT License - * - * Copyright 2023 sebastian. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; - -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toList; - -import io.github.sebastiantoepfer.jsonschema.core.ConstraintViolation; -import io.github.sebastiantoepfer.jsonschema.core.InstanceType; -import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.AnyConstraint; -import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.Constraint; -import jakarta.json.JsonArray; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import java.util.Collection; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; - -/** - * see: https://json-schema.org/understanding-json-schema/reference/type.html - */ -final class Type implements ConstraintAssertion, Constraint { - - private final JsonValue definition; - - public Type(final JsonValue definition) { - this.definition = Objects.requireNonNull(definition); - } - - @Override - public boolean hasName(final String name) { - return Objects.equals("type", name); - } - - @Override - public Collection violationsBy(final JsonValue value) { - return new JsonMappedTypeConstaint(definition).violationsBy(value); - } - - private static final class JsonMappedTypeConstaint implements Constraint { - - private final JsonValue definition; - - public JsonMappedTypeConstaint(final JsonValue definition) { - this.definition = Objects.requireNonNull(definition); - } - - @Override - public Collection violationsBy(final JsonValue value) { - final Constraint typeContraint = - switch (definition.getValueType()) { - case STRING -> new JsonStringTypeConstraint((JsonString) definition); - default -> new JsonArrayTypeConstraint(definition.asJsonArray()); - }; - return typeContraint.violationsBy(value); - } - } - - private static final class JsonArrayTypeConstraint implements Constraint { - - private final JsonArray types; - - public JsonArrayTypeConstraint(final JsonArray types) { - this.types = Objects.requireNonNull(types); - } - - @Override - public Collection violationsBy(final JsonValue value) { - return types - .stream() - .map(JsonMappedTypeConstaint::new) - .collect(collectingAndThen(toList(), AnyConstraint::new)) - .violationsBy(value); - } - } - - private static final class JsonStringTypeConstraint implements Constraint { - - private final String type; - - public JsonStringTypeConstraint(final JsonString type) { - this.type = Objects.requireNonNull(type).getString().toUpperCase(Locale.US); - } - - @Override - public Collection violationsBy(final JsonValue value) { - final InstanceType instanceType = InstanceType.valueOf(type); - final Collection result; - if (instanceType.isInstance(value)) { - result = Set.of(); - } else { - result = Set.of(new ConstraintViolation()); - } - return result; - } - } -} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchema.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchema.java index 74790bab..a9a7ffe1 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchema.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchema.java @@ -29,14 +29,20 @@ import io.github.sebastiantoepfer.jsonschema.core.Validator; import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.AllOfConstraint; import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.Constraint; -import io.github.sebastiantoepfer.jsonschema.core.impl.keyword.Keywords; +import io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core.VocabularyKeywordType; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinition; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinitions; import jakarta.json.JsonObject; import jakarta.json.JsonValue; +import java.util.Collection; -public final class DefaultJsonSchema extends AbstractJsonValueSchema { +final class DefaultJsonSchema extends AbstractJsonValueSchema { + + private final Keywords keywords; public DefaultJsonSchema(final JsonObject value) { super(value); + keywords = new Keywords(vocabulary()); } @Override @@ -44,11 +50,21 @@ public Validator validator() { return asJsonObject() .entrySet() .stream() - .map(Keywords::createKeywordFor) + .map(keywords::createKeywordFor) .filter(Constraint.class::isInstance) .map(k -> (Constraint) k) .collect( collectingAndThen(toList(), constraints -> new DefaultValidator(new AllOfConstraint<>(constraints))) ); } + + private Collection vocabulary() { + return new KeywordSearch(new VocabularyKeywordType()) + .searchForKeywordIn(asJsonObject()) + .filter(VocabularyDefinitions.class::isInstance) + .map(VocabularyDefinitions.class::cast) + .stream() + .flatMap(VocabularyDefinitions::definitions) + .toList(); + } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/KeywordSearch.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/KeywordSearch.java new file mode 100644 index 00000000..0b216ab2 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/KeywordSearch.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.core.impl.spi; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonObject; +import java.util.Objects; +import java.util.Optional; + +final class KeywordSearch { + + private final KeywordType keywordType; + + public KeywordSearch(final KeywordType keywordType) { + this.keywordType = Objects.requireNonNull(keywordType); + } + + public Optional searchForKeywordIn(final JsonObject schema) { + return Optional.ofNullable(schema.get(keywordType.name())).map(keywordType::createKeyword); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/Keywords.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/Keywords.java new file mode 100644 index 00000000..6f747e0d --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/Keywords.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.core.impl.spi; + +import static java.util.function.Predicate.not; +import static java.util.stream.Collectors.toMap; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic.BasicVocabulary; +import io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core.CoreVocabulary; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinition; +import jakarta.json.JsonValue; +import java.net.URI; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; + +final class Keywords { + + public static final Map MANDANTORY_VOCABS; + + static { + MANDANTORY_VOCABS = + List + .of(new BasicVocabulary(), new CoreVocabulary()) + .stream() + .collect(toMap(Vocabulary::id, Function.identity())); + } + + private final Collection vocabularies; + + public Keywords(final Collection vocabDefs) { + if ( + vocabDefs + .stream() + .filter(vocabDef -> MANDANTORY_VOCABS.containsKey(vocabDef.id())) + .anyMatch(not(VocabularyDefinition::required)) + ) { + throw new IllegalArgumentException("can not be created without core vocabulary is requiered!"); + } + vocabularies = + Stream + .concat( + MANDANTORY_VOCABS.values().stream(), + vocabDefs.stream().map(VocabularyDefinition::findVocabulary).flatMap(Optional::stream) + ) + .collect( + Collector.of( + ArrayDeque::new, + ArrayDeque::addFirst, + (first, last) -> null //pitest otherwise see mutants here :( + ) + ); + } + + public Keyword createKeywordFor(final Map.Entry property) { + return vocabularies + .stream() + .map(vocab -> vocab.findKeywordTypeByName(property.getKey())) + .flatMap(Optional::stream) + .findFirst() + .map(keywordType -> keywordType.createKeyword(property.getValue())) + .orElseThrow(); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/ConstraintAssertion.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/ConstraintAssertion.java similarity index 91% rename from core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/ConstraintAssertion.java rename to core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/ConstraintAssertion.java index 14e75640..9b39fb1a 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/ConstraintAssertion.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/ConstraintAssertion.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab; import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.Constraint; import io.github.sebastiantoepfer.jsonschema.core.keyword.Assertion; @@ -30,7 +30,7 @@ /** * only simplify pitest :) */ -interface ConstraintAssertion extends Assertion, Constraint { +public interface ConstraintAssertion extends Assertion, Constraint { @Override default boolean isValidFor(JsonValue instance) { return violationsBy(instance).isEmpty(); diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/BasicVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/BasicVocabulary.java similarity index 57% rename from core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/BasicVocabulary.java rename to core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/BasicVocabulary.java index d5fd1c35..a1ea76cb 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/BasicVocabulary.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/BasicVocabulary.java @@ -21,17 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic; import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; -import io.github.sebastiantoepfer.jsonschema.core.keyword.DefaultAnnotation; -import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; -import jakarta.json.JsonValue; import java.net.URI; import java.util.Optional; -final class BasicVocabulary implements Vocabulary { +public final class BasicVocabulary implements Vocabulary { @Override public URI id() { @@ -40,31 +37,11 @@ public URI id() { @Override public Optional findKeywordTypeByName(final String name) { - KeywordType keywordType = + return Optional.of( switch (name) { - case "type" -> new KeywordType() { - @Override - public String name() { - return "type"; - } - - @Override - public Keyword createKeyword(final JsonValue value) { - return new Type(value); - } - }; - default -> new KeywordType() { - @Override - public String name() { - return name; - } - - @Override - public Keyword createKeyword(final JsonValue value) { - return new DefaultAnnotation(name(), value); - } - }; - }; - return Optional.of(keywordType); + case "type" -> new TypeKeywordType(); + default -> new UnknowKeywordType(name); + } + ); } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/TypeKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/TypeKeywordType.java new file mode 100644 index 00000000..5ec17c60 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/TypeKeywordType.java @@ -0,0 +1,135 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic; + +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; + +import io.github.sebastiantoepfer.jsonschema.core.ConstraintViolation; +import io.github.sebastiantoepfer.jsonschema.core.InstanceType; +import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.AnyConstraint; +import io.github.sebastiantoepfer.jsonschema.core.impl.constraint.Constraint; +import io.github.sebastiantoepfer.jsonschema.core.impl.vocab.ConstraintAssertion; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import java.util.Collection; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; + +/** + * see: https://json-schema.org/understanding-json-schema/reference/type.html + */ +final class TypeKeywordType implements KeywordType { + + @Override + public String name() { + return "type"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new TypeKeyword(value); + } + + private final class TypeKeyword implements ConstraintAssertion, Constraint { + + private final JsonValue definition; + + public TypeKeyword(final JsonValue definition) { + this.definition = Objects.requireNonNull(definition); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public Collection violationsBy(final JsonValue value) { + return new JsonMappedTypeConstaint(definition).violationsBy(value); + } + + private static final class JsonMappedTypeConstaint implements Constraint { + + private final JsonValue definition; + + public JsonMappedTypeConstaint(final JsonValue definition) { + this.definition = Objects.requireNonNull(definition); + } + + @Override + public Collection violationsBy(final JsonValue value) { + final Constraint typeContraint = + switch (definition.getValueType()) { + case STRING -> new JsonStringTypeConstraint((JsonString) definition); + default -> new JsonArrayTypeConstraint(definition.asJsonArray()); + }; + return typeContraint.violationsBy(value); + } + } + + private static final class JsonArrayTypeConstraint implements Constraint { + + private final JsonArray types; + + public JsonArrayTypeConstraint(final JsonArray types) { + this.types = Objects.requireNonNull(types); + } + + @Override + public Collection violationsBy(final JsonValue value) { + return types + .stream() + .map(JsonMappedTypeConstaint::new) + .collect(collectingAndThen(toList(), AnyConstraint::new)) + .violationsBy(value); + } + } + + private static final class JsonStringTypeConstraint implements Constraint { + + private final String type; + + public JsonStringTypeConstraint(final JsonString type) { + this.type = Objects.requireNonNull(type).getString().toUpperCase(Locale.US); + } + + @Override + public Collection violationsBy(final JsonValue value) { + final InstanceType instanceType = InstanceType.valueOf(type); + final Collection result; + if (instanceType.isInstance(value)) { + result = Set.of(); + } else { + result = Set.of(new ConstraintViolation()); + } + return result; + } + } + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/UnknowKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/UnknowKeywordType.java new file mode 100644 index 00000000..3fc2ff44 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/UnknowKeywordType.java @@ -0,0 +1,49 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.DefaultAnnotation; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; +import java.util.Objects; + +final class UnknowKeywordType implements KeywordType { + + private final String name; + + public UnknowKeywordType(final String name) { + this.name = Objects.requireNonNull(name); + } + + @Override + public String name() { + return name; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new DefaultAnnotation(name(), value); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CommentKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CommentKeywordType.java new file mode 100644 index 00000000..4a82a7df --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CommentKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.DefaultAnnotation; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; + +/** + * + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-comments-with-comment + */ +final class CommentKeywordType implements KeywordType { + + @Override + public String name() { + return "$comment"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new DefaultAnnotation(name(), value); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CoreVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CoreVocabulary.java new file mode 100644 index 00000000..efdbe6c9 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CoreVocabulary.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import java.net.URI; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu + */ +public final class CoreVocabulary implements Vocabulary { + + private Collection supportedKeywords = List.of( + new SchemaKeywordType(), + new VocabularyKeywordType(), + new IdKeywordType(), + new RefKeywordType(), + new DynamicRefKeywordType(), + new DefsKeywordType(), + new CommentKeywordType() + ); + + @Override + public URI id() { + //we need a way to find out which draft we are using! + return URI.create("https://json-schema.org/draft/2020-12/vocab/core"); + } + + @Override + public Optional findKeywordTypeByName(final String name) { + return supportedKeywords.stream().filter(keywordType -> keywordType.hasName(name)).findFirst(); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DefsKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DefsKeywordType.java new file mode 100644 index 00000000..e2f4ddea --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DefsKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.DefaultAnnotation; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; + +/** + * + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-schema-re-use-with-defs + */ +final class DefsKeywordType implements KeywordType { + + @Override + public String name() { + return "$defs"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new DefaultAnnotation(name(), value); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DynamicRefKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DynamicRefKeywordType.java new file mode 100644 index 00000000..02a228f3 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DynamicRefKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.Applicator; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; +import java.util.Objects; + +/** + * + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-dynamic-references-with-dyn + */ +final class DynamicRefKeywordType implements KeywordType { + + @Override + public String name() { + return "$dynamicRef"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new Applicator() { + @Override + public boolean applyTo(final JsonValue instance) { + //something is wrong here + return true; + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + }; + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/IdKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/IdKeywordType.java new file mode 100644 index 00000000..e9cdab24 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/IdKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.DefaultAnnotation; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; + +/** + * + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-id-keyword + */ +final class IdKeywordType implements KeywordType { + + @Override + public String name() { + return "$id"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new DefaultAnnotation(name(), value); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/LazyCoreVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/LazyCoreVocabulary.java new file mode 100644 index 00000000..d7719aea --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/LazyCoreVocabulary.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.LazyVocabularies; +import java.net.URI; +import java.util.Objects; +import java.util.Optional; + +public final class LazyCoreVocabulary implements LazyVocabularies { + + private final Vocabulary vocab; + + public LazyCoreVocabulary() { + this.vocab = new CoreVocabulary(); + } + + @Override + public Optional loadVocabularyWithId(final URI id) { + final Optional result; + if (Objects.equals(id, vocab.id())) { + result = Optional.of(vocab); + } else { + result = Optional.empty(); + } + return result; + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/RefKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/RefKeywordType.java new file mode 100644 index 00000000..39785745 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/RefKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.Applicator; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; +import java.util.Objects; + +/** + * + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-direct-references-with-ref + */ +final class RefKeywordType implements KeywordType { + + @Override + public String name() { + return "$ref"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new Applicator() { + @Override + public boolean applyTo(final JsonValue instance) { + return true; //something is wrong here + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + }; + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/SchemaKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/SchemaKeywordType.java new file mode 100644 index 00000000..2e110175 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/SchemaKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.DefaultAnnotation; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import jakarta.json.JsonValue; + +/** + * + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-schema-keyword + */ +final class SchemaKeywordType implements KeywordType { + + @Override + public String name() { + return "$schema"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + return new DefaultAnnotation(name(), value); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/VocabularyKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/VocabularyKeywordType.java new file mode 100644 index 00000000..bd52fd79 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/VocabularyKeywordType.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.core.impl.vocab.core; + +import io.github.sebastiantoepfer.jsonschema.core.InstanceType; +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinition; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinitions; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import java.net.URI; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-vocabulary-keyword + */ +public final class VocabularyKeywordType implements KeywordType { + + @Override + public String name() { + return "$vocabulary"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + final Keyword result; + if (InstanceType.OBJECT.isInstance(value)) { + result = new VocabularyKeyword(value); + } else { + throw new IllegalArgumentException( + "must be an object! " + + "read https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-vocabulary-keyword" + + "for more infromations" + ); + } + return result; + } + + public final class VocabularyKeyword implements Keyword, VocabularyDefinitions { + + private final JsonObject vocabularies; + + VocabularyKeyword(final JsonValue vocabularies) { + this(vocabularies.asJsonObject()); + } + + VocabularyKeyword(final JsonObject vocabularies) { + this.vocabularies = Objects.requireNonNull(vocabularies); + } + + @Override + public Collection categories() { + return List.of(); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public Stream definitions() { + return vocabularies + .entrySet() + .stream() + .map(entry -> new VocabularyDefinition(URI.create(entry.getKey()), entry.getValue() == JsonValue.TRUE)); + } + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/LazyVocabularies.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/LazyVocabularies.java new file mode 100644 index 00000000..44e97301 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/LazyVocabularies.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.spi; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import java.net.URI; +import java.util.Optional; + +public interface LazyVocabularies { + default boolean knowsId(URI id) { + return loadVocabularyWithId(id).isPresent(); + } + + Optional loadVocabularyWithId(URI id); +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinition.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinition.java new file mode 100644 index 00000000..667b4b9b --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinition.java @@ -0,0 +1,45 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.spi; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import java.net.URI; +import java.util.Optional; +import java.util.ServiceLoader; + +public record VocabularyDefinition(URI id, boolean required) { + public Optional findVocabulary() { + final Optional result = ServiceLoader + .load(LazyVocabularies.class) + .stream() + .map(ServiceLoader.Provider::get) + .map(loader -> loader.loadVocabularyWithId(id)) + .flatMap(Optional::stream) + .findFirst(); + if (result.isEmpty() && required) { + throw new IllegalStateException("can not find required vocabulary: " + id); + } + return result; + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinitions.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinitions.java new file mode 100644 index 00000000..af904b01 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinitions.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.core.vocab.spi; + +import java.util.stream.Stream; + +public interface VocabularyDefinitions { + Stream definitions(); +} diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index 99eafb82..beff4b69 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -25,5 +25,10 @@ exports io.github.sebastiantoepfer.jsonschema.core; exports io.github.sebastiantoepfer.jsonschema.core.keyword; + exports io.github.sebastiantoepfer.jsonschema.core.vocab.spi; + + provides io.github.sebastiantoepfer.jsonschema.core.vocab.spi.LazyVocabularies + with io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core.LazyCoreVocabulary; + requires jakarta.json; } diff --git a/core/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.core.vocab.spi.LazyVocabularies b/core/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.core.vocab.spi.LazyVocabularies new file mode 100644 index 00000000..6e2e70b1 --- /dev/null +++ b/core/src/main/resources/META-INF/services/io.github.sebastiantoepfer.jsonschema.core.vocab.spi.LazyVocabularies @@ -0,0 +1 @@ +io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core.LazyCoreVocabulary diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchemaTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchemaTest.java index 3c24f818..d94a2bfc 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchemaTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/DefaultJsonSchemaTest.java @@ -29,7 +29,9 @@ import static org.hamcrest.Matchers.not; import jakarta.json.Json; +import jakarta.json.JsonObject; import jakarta.json.JsonValue; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class DefaultJsonSchemaTest { @@ -47,4 +49,17 @@ void should_be_valid_for_string() { void should_be_invalid_for_object() { assertThat(schema.validator().validate(JsonValue.EMPTY_JSON_OBJECT), is(not(empty()))); } + + @Test + void should_not_be_loadable_without_mandantory_core_vocabulary() { + final JsonObject invalidSchema = Json + .createObjectBuilder() + .add( + "$vocabulary", + Json.createObjectBuilder().add("https://json-schema.org/draft/2020-12/vocab/core", false) + ) + .build(); + + Assertions.assertThrows(Exception.class, () -> new DefaultJsonSchema(invalidSchema)); + } } diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/KeywordsTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/KeywordsTest.java new file mode 100644 index 00000000..c8ffda57 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/spi/KeywordsTest.java @@ -0,0 +1,65 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.impl.spi; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic.BasicVocabulary; +import io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core.CoreVocabulary; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinition; +import java.net.URI; +import java.util.Collection; +import java.util.List; +import org.junit.jupiter.api.Test; + +class KeywordsTest { + + @Test + void should_not_be_createbale_without_mandantory_core_vocabulary() { + final Collection vocabDefs = List.of( + new VocabularyDefinition(new CoreVocabulary().id(), false) + ); + assertThrows(IllegalArgumentException.class, () -> new Keywords(vocabDefs)); + } + + @Test + void should_not_be_createbale_without_mandantory_base_vocabulary() { + final Collection vocabDefs = List.of( + new VocabularyDefinition(new BasicVocabulary().id(), false) + ); + assertThrows(IllegalArgumentException.class, () -> new Keywords(vocabDefs)); + } + + @Test + void should_be_createbale_with_optional_vocabularies() { + assertThat( + new Keywords(List.of(new VocabularyDefinition(URI.create("http://optinal"), false))), + is(not(nullValue())) + ); + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/ConstraintAssertionTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/ConstraintAssertionTest.java similarity index 97% rename from core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/ConstraintAssertionTest.java rename to core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/ConstraintAssertionTest.java index 15abc7af..7c4b52af 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/ConstraintAssertionTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/ConstraintAssertionTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/BasicVocabularyTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/BasicVocabularyTest.java similarity index 97% rename from core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/BasicVocabularyTest.java rename to core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/BasicVocabularyTest.java index 8675dc79..260772a6 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/BasicVocabularyTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/BasicVocabularyTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/TypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/TypeTest.java similarity index 78% rename from core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/TypeTest.java rename to core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/TypeTest.java index 14009169..12ddb326 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/TypeTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/basic/TypeTest.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.basic; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -35,17 +35,17 @@ class TypeTest { @Test void should_know_his_name() { - assertThat(new Type(Json.createValue("string")).hasName("type"), is(true)); + assertThat(new TypeKeywordType().createKeyword(Json.createValue("string")).hasName("type"), is(true)); } @Test void should_know_other_names() { - assertThat(new Type(Json.createValue("string")).hasName("id"), is(false)); + assertThat(new TypeKeywordType().createKeyword(Json.createValue("string")).hasName("id"), is(false)); } @Test void should_use_stringvalue_to_validate_type() { - final Assertion typeAssertion = new Type(Json.createValue("string")).asAssertion(); + final Assertion typeAssertion = new TypeKeywordType().createKeyword(Json.createValue("string")).asAssertion(); assertThat(typeAssertion.isValidFor(Json.createValue("value")), is(true)); assertThat(typeAssertion.isValidFor(JsonValue.EMPTY_JSON_OBJECT), is(false)); @@ -55,9 +55,10 @@ void should_use_stringvalue_to_validate_type() { @Test void should_use_arrayvalue_to_validate_type() { - final Assertion typeAssertion = new Type( - Json.createArrayBuilder().add(Json.createValue("string")).add(Json.createValue("object")).build() - ) + final Assertion typeAssertion = new TypeKeywordType() + .createKeyword( + Json.createArrayBuilder().add(Json.createValue("string")).add(Json.createValue("object")).build() + ) .asAssertion(); assertThat(typeAssertion.isValidFor(Json.createValue("value")), is(true)); diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CoreVocabularyTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CoreVocabularyTest.java new file mode 100644 index 00000000..2b8febe9 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/CoreVocabularyTest.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.core.impl.vocab.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import com.github.npathai.hamcrestopt.OptionalMatchers; +import jakarta.json.JsonValue; +import java.net.URI; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class CoreVocabularyTest { + + void should_return_the_uri_of_the_current_core_vocabulary() { + assertThat(new CoreVocabulary().id(), is(URI.create("https://json-schema.org/draft/2020-12/vocab/core"))); + } + + @ParameterizedTest(name = "should know keyword {0}") + @ValueSource(strings = { "$schema", "$vocabulary", "$id", "$ref", "$dynamicRef", "$defs", "$comment" }) + void should_return_keywords_for_name(final String name) { + //keyword creation for easier pitesting :) -> i know it is bad + assertThat( + new CoreVocabulary() + .findKeywordTypeByName(name) + .map(keywordType -> keywordType.createKeyword(JsonValue.EMPTY_JSON_OBJECT)) + .map(keyword -> keyword.hasName(name)), + OptionalMatchers.isPresentAndIs(true) + ); + } +} diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/Keywords.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DynamicRefKeywordTypeTest.java similarity index 67% rename from core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/Keywords.java rename to core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DynamicRefKeywordTypeTest.java index 2a81121e..3f3cd474 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/impl/keyword/Keywords.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/DynamicRefKeywordTypeTest.java @@ -21,25 +21,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package io.github.sebastiantoepfer.jsonschema.core.impl.keyword; +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; import jakarta.json.JsonValue; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; +import org.junit.jupiter.api.Test; -public final class Keywords { +class DynamicRefKeywordTypeTest { - public static Keyword createKeywordFor(final Map.Entry property) { - return Stream - .of(new BasicVocabulary()) - .map(vocab -> vocab.findKeywordTypeByName(property.getKey())) - .flatMap(Optional::stream) - .findFirst() - .map(keywordType -> keywordType.createKeyword(property.getValue())) - .orElseThrow(); - } + @Test + void notFinischedYet() { + final Keyword keyword = new DynamicRefKeywordType().createKeyword(JsonValue.FALSE); - private Keywords() {} + assertThat(keyword.hasName("$dynamicRef"), is(true)); + assertThat(keyword.hasName("$id"), is(false)); + + assertThat(keyword.asApplicator().applyTo(JsonValue.TRUE), is(true)); + } } diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/RefKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/RefKeywordTypeTest.java new file mode 100644 index 00000000..db079cb3 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/RefKeywordTypeTest.java @@ -0,0 +1,44 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.impl.vocab.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import jakarta.json.JsonValue; +import org.junit.jupiter.api.Test; + +class RefKeywordTypeTest { + + @Test + void notFinischedYet() { + final Keyword keyword = new RefKeywordType().createKeyword(JsonValue.FALSE); + + assertThat(keyword.hasName("$ref"), is(true)); + assertThat(keyword.hasName("$id"), is(false)); + + assertThat(keyword.asApplicator().applyTo(JsonValue.TRUE), is(true)); + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/VocabularyKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/VocabularyKeywordTypeTest.java new file mode 100644 index 00000000..5772ce28 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/impl/vocab/core/VocabularyKeywordTypeTest.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.core.impl.vocab.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.core.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinition; +import io.github.sebastiantoepfer.jsonschema.core.vocab.spi.VocabularyDefinitions; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import java.net.URI; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class VocabularyKeywordTypeTest { + + @Test + void should_not_create_keyword_for_non_jsonobject() { + final VocabularyKeywordType keywordType = new VocabularyKeywordType(); + Assertions.assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(JsonValue.FALSE)); + } + + @Test + void should_created_keyword_should_know_his_name() { + final Keyword vocabulary = new VocabularyKeywordType().createKeyword(JsonValue.EMPTY_JSON_OBJECT); + + assertThat(vocabulary.hasName("$vocabulary"), is(true)); + assertThat(vocabulary.hasName("$id"), is(false)); + } + + @Test + void should_create_definitions() { + assertThat( + ((VocabularyDefinitions) new VocabularyKeywordType() + .createKeyword( + Json + .createObjectBuilder() + .add("http://json-schema.org/test", true) + .add("http://openapi.org/test", false) + .build() + )).definitions() + .toList(), + containsInAnyOrder( + new VocabularyDefinition(URI.create("http://json-schema.org/test"), true), + new VocabularyDefinition(URI.create("http://openapi.org/test"), false) + ) + ); + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/LazyVocabulariesTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/LazyVocabulariesTest.java new file mode 100644 index 00000000..332ce93c --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/LazyVocabulariesTest.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.spi; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import io.github.sebastiantoepfer.jsonschema.core.keyword.KeywordType; +import java.net.URI; +import java.util.Objects; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class LazyVocabulariesTest implements LazyVocabularies { + + @Test + void should_know_which_vocabularies_are_supported() { + assertThat(knowsId(URI.create("http://github.com/sebastian-toepfer/json-schema/basic")), is(true)); + assertThat(knowsId(URI.create("http://invalid")), is(false)); + } + + @Override + public Optional loadVocabularyWithId(final URI id) { + final Optional result; + if (Objects.equals(URI.create("http://github.com/sebastian-toepfer/json-schema/basic"), id)) { + result = + Optional.of( + new Vocabulary() { + @Override + public URI id() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Optional findKeywordTypeByName(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + ); + } else { + result = Optional.empty(); + } + return result; + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinitionTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinitionTest.java new file mode 100644 index 00000000..7e096d93 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/spi/VocabularyDefinitionTest.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.core.vocab.spi; + +import static com.github.npathai.hamcrestopt.OptionalMatchers.isEmpty; +import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresentAndIs; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.github.sebastiantoepfer.jsonschema.core.Vocabulary; +import java.net.URI; +import org.junit.jupiter.api.Test; + +class VocabularyDefinitionTest { + + @Test + void should_throw_illegal_state_if_a_required_vocabulary_can_not_be_loaded() { + final VocabularyDefinition vocabDef = new VocabularyDefinition(URI.create("https://invalid"), true); + assertThrows(IllegalStateException.class, () -> vocabDef.findVocabulary()); + } + + @Test + void should_find_mandatory_core_vocabulary() { + assertThat( + new VocabularyDefinition(URI.create("https://json-schema.org/draft/2020-12/vocab/core"), true) + .findVocabulary() + .map(Vocabulary::id), + isPresentAndIs(URI.create("https://json-schema.org/draft/2020-12/vocab/core")) + ); + } + + @Test + void should_retrun_empty_for_optional_vocabulary_which_can_not_be_loaded() { + assertThat( + new VocabularyDefinition(URI.create("https://invalid"), false).findVocabulary().map(Vocabulary::id), + isEmpty() + ); + } +}