diff --git a/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/Annotation.java b/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/Annotation.java index f7e2ae47..0404c174 100644 --- a/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/Annotation.java +++ b/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/Annotation.java @@ -38,5 +38,5 @@ default Collection categories() { return Set.of(KeywordCategory.ANNOTATION); } - JsonValue value(); + JsonValue valueFor(JsonValue value); } diff --git a/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/DefaultAnnotation.java b/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/StaticAnnotation.java similarity index 90% rename from api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/DefaultAnnotation.java rename to api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/StaticAnnotation.java index 52ba76c3..fa8b7559 100644 --- a/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/DefaultAnnotation.java +++ b/api/src/main/java/io/github/sebastiantoepfer/jsonschema/keyword/StaticAnnotation.java @@ -32,12 +32,12 @@ * * see: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-objects-and-key */ -public final class DefaultAnnotation implements Annotation { +public final class StaticAnnotation implements Annotation { private final String name; private final JsonValue value; - public DefaultAnnotation(final String name, final JsonValue value) { + public StaticAnnotation(final String name, final JsonValue value) { this.name = Objects.requireNonNull(name); this.value = Objects.requireNonNullElse(value, JsonValue.NULL); } @@ -48,7 +48,7 @@ public boolean hasName(final String name) { } @Override - public JsonValue value() { + public JsonValue valueFor(final JsonValue instance) { return value; } } diff --git a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/AnnotationTest.java b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/AnnotationTest.java index a1998142..c9479d12 100644 --- a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/AnnotationTest.java +++ b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/AnnotationTest.java @@ -52,7 +52,7 @@ void should_return_this_as_annotation() { private static class TestAnnotation implements Annotation { @Override - public JsonValue value() { + public JsonValue valueFor(JsonValue value) { throw new UnsupportedOperationException("Not supported yet."); } diff --git a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/DefaultAnnotationTest.java b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/StaticAnnotationTest.java similarity index 77% rename from api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/DefaultAnnotationTest.java rename to api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/StaticAnnotationTest.java index 775cbb66..89206c70 100644 --- a/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/DefaultAnnotationTest.java +++ b/api/src/test/java/io/github/sebastiantoepfer/jsonschema/keyword/StaticAnnotationTest.java @@ -27,22 +27,26 @@ import static org.hamcrest.Matchers.is; import jakarta.json.Json; +import jakarta.json.JsonValue; import org.junit.jupiter.api.Test; -class DefaultAnnotationTest { +class StaticAnnotationTest { @Test void should_know_his_name() { - assertThat(new DefaultAnnotation("myname", Json.createValue("string")).hasName("myname"), is(true)); + assertThat(new StaticAnnotation("myname", Json.createValue("string")).hasName("myname"), is(true)); } @Test void should_know_other_names() { - assertThat(new DefaultAnnotation("myname", Json.createValue("string")).hasName("id"), is(false)); + assertThat(new StaticAnnotation("myname", Json.createValue("string")).hasName("id"), is(false)); } @Test void should_return_his_value() { - assertThat(new DefaultAnnotation("myname", Json.createValue("string")).value(), is(Json.createValue("string"))); + assertThat( + new StaticAnnotation("myname", Json.createValue("string")).valueFor(JsonValue.FALSE), + is(Json.createValue("string")) + ); } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/UnknowKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/UnknowKeywordType.java index a23d3541..26d02923 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/UnknowKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/UnknowKeywordType.java @@ -24,9 +24,9 @@ package io.github.sebastiantoepfer.jsonschema.core; import io.github.sebastiantoepfer.jsonschema.JsonSchema; -import io.github.sebastiantoepfer.jsonschema.keyword.DefaultAnnotation; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import io.github.sebastiantoepfer.jsonschema.keyword.StaticAnnotation; import jakarta.json.JsonValue; import java.util.Objects; @@ -45,6 +45,6 @@ public String name() { @Override public Keyword createKeyword(final JsonSchema schema, final JsonValue value) { - return new DefaultAnnotation(name(), value); + return new StaticAnnotation(name(), value); } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordType.java index 7635198b..cf404f16 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordType.java @@ -93,9 +93,9 @@ public Collection categories() { } @Override - public JsonValue value() { + public JsonValue valueFor(final JsonValue value) { final JsonValue result; - if (appliesToAny()) { + if (appliesToAnyFor(value.asJsonArray())) { result = JsonValue.TRUE; } else { result = JsonValue.FALSE; @@ -103,8 +103,8 @@ public JsonValue value() { return result; } - private boolean appliesToAny() { - return startIndex() < 0; + private boolean appliesToAnyFor(final JsonArray value) { + return startIndexFor(value) == -1; } @Override @@ -114,18 +114,42 @@ public boolean applyTo(final JsonValue instance) { private boolean matchesSchema(final JsonArray items) { final Validator itemValidator = validator(); - return items.stream().skip(startIndex() + 1).map(itemValidator::validate).allMatch(Collection::isEmpty); + return items + .stream() + .skip(startIndexFor(items) + 1L) + .map(itemValidator::validate) + .allMatch(Collection::isEmpty); } - private long startIndex() { + private int startIndexFor(final JsonArray value) { return owner() .keywordByName("prefixItems") .map(Keyword::asAnnotation) - .map(Annotation::value) - .filter(InstanceType.INTEGER::isInstance) - .map(JsonNumber.class::cast) - .map(JsonNumber::longValue) - .orElse(-1L); + .map(anno -> anno.valueFor(value)) + .map(v -> new MaxIndexCalculator(value, v)) + .map(MaxIndexCalculator::maxIndex) + .orElse(-1); + } + + private static class MaxIndexCalculator { + + private final JsonArray array; + private final JsonValue index; + + public MaxIndexCalculator(final JsonArray array, final JsonValue index) { + this.array = array; + this.index = index; + } + + int maxIndex() { + final int result; + if (index == JsonValue.TRUE) { + result = array.size(); + } else { + result = ((JsonNumber) index).intValue(); + } + return result; + } } } } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordType.java index 303e752c..fcb028c9 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordType.java @@ -76,8 +76,14 @@ public boolean hasName(final String name) { } @Override - public JsonValue value() { - return Json.createValue(schemas.size() - 1); + public JsonValue valueFor(final JsonValue value) { + final JsonValue result; + if (value.asJsonArray().size() == schemas.size()) { + result = JsonValue.TRUE; + } else { + result = Json.createValue(Math.min(value.asJsonArray().size(), schemas.size()) - 1); + } + return result; } @Override diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/CommentKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/CommentKeywordType.java index bcace34a..ce060338 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/CommentKeywordType.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/core/CommentKeywordType.java @@ -24,9 +24,9 @@ package io.github.sebastiantoepfer.jsonschema.core.vocab.core; import io.github.sebastiantoepfer.jsonschema.JsonSchema; -import io.github.sebastiantoepfer.jsonschema.keyword.DefaultAnnotation; import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import io.github.sebastiantoepfer.jsonschema.keyword.StaticAnnotation; import jakarta.json.JsonValue; /** @@ -42,6 +42,6 @@ public String name() { @Override public Keyword createKeyword(final JsonSchema schema, final JsonValue value) { - return new DefaultAnnotation(name(), value); + return new StaticAnnotation(name(), value); } } diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordTypeTest.java index c224d091..af44eb12 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordTypeTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/ItemsKeywordTypeTest.java @@ -114,7 +114,7 @@ void should_produces_true_if_is_applied_to_any_instance() { new ItemsKeywordType() .createKeyword(new DefaultJsonSchemaFactory().create(JsonValue.TRUE), JsonValue.EMPTY_JSON_OBJECT) .asAnnotation() - .value(), + .valueFor(Json.createArrayBuilder().add(1).build()), is(JsonValue.TRUE) ); } @@ -155,7 +155,7 @@ void should_return_false_if_not_applies_to_any_item() { JsonValue.FALSE ) .asAnnotation() - .value(), + .valueFor(Json.createArrayBuilder().add(1).build()), is(JsonValue.FALSE) ); } @@ -167,12 +167,15 @@ void should_be_valid_if_invaliditem_is_already_checked_by_prefixItems() { .createKeyword( new DefaultJsonSchemaFactory() .create( - Json.createObjectBuilder().add("prefixItems", Json.createArrayBuilder().add(true)).build() + Json + .createObjectBuilder() + .add("prefixItems", Json.createArrayBuilder().add(true).add(true)) + .build() ), - JsonValue.FALSE + Json.createObjectBuilder().add("type", "integer").build() ) .asApplicator() - .applyTo(Json.createArrayBuilder().add("1").build()), + .applyTo(Json.createArrayBuilder().add("1").add("2").add(1).build()), is(true) ); } @@ -186,10 +189,10 @@ void should_be_invalid_if_invaliditem_is_not_already_checked_by_prefixItems() { .create( Json.createObjectBuilder().add("prefixItems", Json.createArrayBuilder().add(true)).build() ), - JsonValue.FALSE + Json.createObjectBuilder().add("type", "integer").build() ) .asApplicator() - .applyTo(Json.createArrayBuilder().add("1").add("2").build()), + .applyTo(Json.createArrayBuilder().add("1").add("2").add(1).build()), is(false) ); } diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordTypeTest.java index c1c2855f..4dc81dfd 100644 --- a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordTypeTest.java +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/applicator/PrefixItemsKeywordTypeTest.java @@ -62,7 +62,7 @@ void should_not_be_createbale_from_non_array() { } @Test - void should_return_one_as_value() { + void should_return_zero_as_value() { assertThat( new PrefixItemsKeywordType() .createKeyword( @@ -70,11 +70,25 @@ void should_return_one_as_value() { Json.createArrayBuilder().add(JsonValue.TRUE).build() ) .asAnnotation() - .value(), + .valueFor(Json.createArrayBuilder().add(1).add(2).build()), is(Json.createValue(0)) ); } + @Test + void should_retrun_true_if_is_applies_to_all_values() { + assertThat( + new PrefixItemsKeywordType() + .createKeyword( + new DefaultJsonSchemaFactory().create(JsonValue.TRUE), + Json.createArrayBuilder().add(JsonValue.TRUE).add(JsonValue.TRUE).build() + ) + .asAnnotation() + .valueFor(Json.createArrayBuilder().add(1).add(2).build()), + is(JsonValue.TRUE) + ); + } + @Test void should_be_valid_for_non_arrays() { assertThat(