From afead32e7bf7c56e337f48ce524f449fb9b262af Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:26:20 +0200 Subject: [PATCH 1/4] add support for minimum keyword --- core/pom.xml | 6 ++ .../core/vocab/basic/BasicVocabulary.java | 4 +- .../core/vocab/basic/MinimumKeywordType.java | 79 +++++++++++++++++ .../vocab/basic/MinimumKeywordTypeTest.java | 85 +++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordType.java create mode 100644 core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordTypeTest.java diff --git a/core/pom.xml b/core/pom.xml index dffca747..7fe70c57 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -136,6 +136,12 @@ **/tests/draft2019-09/pattern.json **/tests/draft2020-12/pattern.json **/tests/draft-next/pattern.json + + **/tests/draft6/minimum.json + **/tests/draft7/minimum.json + **/tests/draft2019-09/minimum.json + **/tests/draft2020-12/minimum.json + **/tests/draft-next/minimum.json diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java index 4c090c5b..c7f23289 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java @@ -40,7 +40,9 @@ public BasicVocabulary() { new TypeKeywordType(), new MinLengthKeywordType(), new MaxLengthKeywordType(), - new PatternKeywordType() + new PatternKeywordType(), + new PatternKeywordType(), + new MinimumKeywordType() ); } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordType.java new file mode 100644 index 00000000..d923a363 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordType.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.basic; + +import io.github.sebastiantoepfer.jsonschema.ConstraintViolation; +import io.github.sebastiantoepfer.jsonschema.InstanceType; +import io.github.sebastiantoepfer.jsonschema.core.vocab.ConstraintAssertion; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.JsonNumber; +import jakarta.json.JsonValue; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +final class MinimumKeywordType implements KeywordType { + + @Override + public String name() { + return "minimum"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + if (InstanceType.NUMBER.isInstance(value)) { + return new MinimumKeyword((JsonNumber) value); + } else { + throw new IllegalArgumentException("must be a number"); + } + } + + private class MinimumKeyword implements ConstraintAssertion { + + private final BigDecimal min; + + public MinimumKeyword(final JsonNumber min) { + this.min = min.bigDecimalValue(); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public Collection violationsBy(final JsonValue value) { + final Collection result; + if (!InstanceType.NUMBER.isInstance(value) || min.compareTo(((JsonNumber) value).bigDecimalValue()) <= 0) { + result = Collections.emptyList(); + } else { + result = List.of(new ConstraintViolation()); + } + return result; + } + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordTypeTest.java new file mode 100644 index 00000000..59b976d7 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MinimumKeywordTypeTest.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.core.vocab.basic; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class MinimumKeywordTypeTest { + + @Test + void should_know_his_name() { + final Keyword minimum = new MinimumKeywordType().createKeyword(Json.createValue(1)); + + assertThat(minimum.hasName("minimum"), is(true)); + assertThat(minimum.hasName("test"), is(false)); + } + + @Test + void should_not_becreatable_with_non_number() { + final MinimumKeywordType keywordType = new MinimumKeywordType(); + Assertions.assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(JsonValue.FALSE)); + } + + @Test + void should_be_valid_for_non_number_values() { + assertThat( + new MinimumKeywordType() + .createKeyword(Json.createValue(1)) + .asAssertion() + .isValidFor(JsonValue.EMPTY_JSON_OBJECT), + is(true) + ); + } + + @Test + void should_be_invalid_for_smaller_numbers() { + assertThat( + new MinimumKeywordType().createKeyword(Json.createValue(0)).asAssertion().isValidFor(Json.createValue(-1)), + is(false) + ); + } + + @Test + void should_be_valid_for_equals_numbers() { + assertThat( + new MinimumKeywordType().createKeyword(Json.createValue(0)).asAssertion().isValidFor(Json.createValue(0)), + is(true) + ); + } + + @Test + void shhould_be_valid_for_greater_numbers() { + assertThat( + new MinimumKeywordType().createKeyword(Json.createValue(0)).asAssertion().isValidFor(Json.createValue(1)), + is(true) + ); + } +} From 825d63300ae2c73ed380df7d80dfd0bbf92d0a5a Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:26:20 +0200 Subject: [PATCH 2/4] add support for exclusiveMinimum keyword --- core/pom.xml | 6 ++ .../core/vocab/basic/BasicVocabulary.java | 3 +- .../basic/ExclusiveMinimumKeywordType.java | 79 ++++++++++++++++ .../ExclusiveMinimumKeywordTypeTest.java | 94 +++++++++++++++++++ 4 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordType.java create mode 100644 core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordTypeTest.java diff --git a/core/pom.xml b/core/pom.xml index 7fe70c57..14212cf3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -142,6 +142,12 @@ **/tests/draft2019-09/minimum.json **/tests/draft2020-12/minimum.json **/tests/draft-next/minimum.json + + **/tests/draft6/exclusiveMinimum.json + **/tests/draft7/exclusiveMinimum.json + **/tests/draft2019-09/exclusiveMinimum.json + **/tests/draft2020-12/exclusiveMinimum.json + **/tests/draft-next/exclusiveMinimum.json diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java index c7f23289..2f690086 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java @@ -42,7 +42,8 @@ public BasicVocabulary() { new MaxLengthKeywordType(), new PatternKeywordType(), new PatternKeywordType(), - new MinimumKeywordType() + new MinimumKeywordType(), + new ExclusiveMinimumKeywordType() ); } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordType.java new file mode 100644 index 00000000..5cea078d --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordType.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.basic; + +import io.github.sebastiantoepfer.jsonschema.ConstraintViolation; +import io.github.sebastiantoepfer.jsonschema.InstanceType; +import io.github.sebastiantoepfer.jsonschema.core.vocab.ConstraintAssertion; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.JsonNumber; +import jakarta.json.JsonValue; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +final class ExclusiveMinimumKeywordType implements KeywordType { + + @Override + public String name() { + return "exclusiveMinimum"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + if (InstanceType.NUMBER.isInstance(value)) { + return new ExclusiveMinimumKeyword((JsonNumber) value); + } else { + throw new IllegalArgumentException("must be a number!"); + } + } + + private class ExclusiveMinimumKeyword implements ConstraintAssertion { + + private final BigDecimal min; + + public ExclusiveMinimumKeyword(final JsonNumber min) { + this.min = min.bigDecimalValue(); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public Collection violationsBy(final JsonValue value) { + final Collection result; + if (!InstanceType.NUMBER.isInstance(value) || min.compareTo(((JsonNumber) value).bigDecimalValue()) < 0) { + result = Collections.emptyList(); + } else { + result = List.of(new ConstraintViolation()); + } + return result; + } + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordTypeTest.java new file mode 100644 index 00000000..004abf7b --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMinimumKeywordTypeTest.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.vocab.basic; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ExclusiveMinimumKeywordTypeTest { + + @Test + void should_know_his_name() { + final Keyword minimum = new ExclusiveMinimumKeywordType().createKeyword(Json.createValue(1)); + + assertThat(minimum.hasName("exclusiveMinimum"), is(true)); + assertThat(minimum.hasName("test"), is(false)); + } + + @Test + void should_not_becreatable_with_non_number() { + final ExclusiveMinimumKeywordType keywordType = new ExclusiveMinimumKeywordType(); + Assertions.assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(JsonValue.FALSE)); + } + + @Test + void should_be_valid_for_non_number_values() { + assertThat( + new ExclusiveMinimumKeywordType() + .createKeyword(Json.createValue(1)) + .asAssertion() + .isValidFor(JsonValue.EMPTY_JSON_OBJECT), + is(true) + ); + } + + @Test + void should_be_invalid_for_smaller_numbers() { + assertThat( + new ExclusiveMinimumKeywordType() + .createKeyword(Json.createValue(0)) + .asAssertion() + .isValidFor(Json.createValue(-1)), + is(false) + ); + } + + @Test + void should_be_invalid_for_equals_numbers() { + assertThat( + new ExclusiveMinimumKeywordType() + .createKeyword(Json.createValue(0)) + .asAssertion() + .isValidFor(Json.createValue(0)), + is(false) + ); + } + + @Test + void shhould_be_valid_for_greater_numbers() { + assertThat( + new ExclusiveMinimumKeywordType() + .createKeyword(Json.createValue(0)) + .asAssertion() + .isValidFor(Json.createValue(1)), + is(true) + ); + } +} From baa8c46b6e96802ccdad46a6e535fbecdff8f506 Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:26:20 +0200 Subject: [PATCH 3/4] add support for maximum keyword --- core/pom.xml | 6 ++ .../core/vocab/basic/BasicVocabulary.java | 3 +- .../core/vocab/basic/MaximumKeywordType.java | 79 +++++++++++++++++ .../vocab/basic/MaximumKeywordTypeTest.java | 85 +++++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordType.java create mode 100644 core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordTypeTest.java diff --git a/core/pom.xml b/core/pom.xml index 14212cf3..97a1188f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -148,6 +148,12 @@ **/tests/draft2019-09/exclusiveMinimum.json **/tests/draft2020-12/exclusiveMinimum.json **/tests/draft-next/exclusiveMinimum.json + + **/tests/draft6/maximum.json + **/tests/draft7/maximum.json + **/tests/draft2019-09/maximum.json + **/tests/draft2020-12/maximum.json + **/tests/draft-next/maximum.json diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java index 2f690086..f00429ad 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java @@ -43,7 +43,8 @@ public BasicVocabulary() { new PatternKeywordType(), new PatternKeywordType(), new MinimumKeywordType(), - new ExclusiveMinimumKeywordType() + new ExclusiveMinimumKeywordType(), + new MaximumKeywordType() ); } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordType.java new file mode 100644 index 00000000..e41d8463 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordType.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.basic; + +import io.github.sebastiantoepfer.jsonschema.ConstraintViolation; +import io.github.sebastiantoepfer.jsonschema.InstanceType; +import io.github.sebastiantoepfer.jsonschema.core.vocab.ConstraintAssertion; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.JsonNumber; +import jakarta.json.JsonValue; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +final class MaximumKeywordType implements KeywordType { + + @Override + public String name() { + return "maximum"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + if (InstanceType.NUMBER.isInstance(value)) { + return new MaximumKeyword((JsonNumber) value); + } else { + throw new IllegalArgumentException("must be a number!"); + } + } + + private class MaximumKeyword implements ConstraintAssertion { + + private final BigDecimal max; + + public MaximumKeyword(final JsonNumber max) { + this.max = max.bigDecimalValue(); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public Collection violationsBy(final JsonValue value) { + final Collection result; + if (!InstanceType.NUMBER.isInstance(value) || max.compareTo(((JsonNumber) value).bigDecimalValue()) >= 0) { + result = Collections.emptyList(); + } else { + result = List.of(new ConstraintViolation()); + } + return result; + } + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordTypeTest.java new file mode 100644 index 00000000..0e9f4b45 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/MaximumKeywordTypeTest.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.core.vocab.basic; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class MaximumKeywordTypeTest { + + @Test + void should_know_his_name() { + final Keyword maximum = new MaximumKeywordType().createKeyword(Json.createValue(1)); + + assertThat(maximum.hasName("maximum"), is(true)); + assertThat(maximum.hasName("test"), is(false)); + } + + @Test + void should_not_becreatable_with_non_number() { + final MaximumKeywordType keywordType = new MaximumKeywordType(); + Assertions.assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(JsonValue.FALSE)); + } + + @Test + void should_be_valid_for_non_number_values() { + assertThat( + new MaximumKeywordType() + .createKeyword(Json.createValue(1)) + .asAssertion() + .isValidFor(JsonValue.EMPTY_JSON_OBJECT), + is(true) + ); + } + + @Test + void should_be_invalid_for_greater_numbers() { + assertThat( + new MaximumKeywordType().createKeyword(Json.createValue(10)).asAssertion().isValidFor(Json.createValue(11)), + is(false) + ); + } + + @Test + void should_be_valid_for_equals_numbers() { + assertThat( + new MaximumKeywordType().createKeyword(Json.createValue(10)).asAssertion().isValidFor(Json.createValue(10)), + is(true) + ); + } + + @Test + void shhould_be_valid_for_smaller_numbers() { + assertThat( + new MaximumKeywordType().createKeyword(Json.createValue(10)).asAssertion().isValidFor(Json.createValue(9)), + is(true) + ); + } +} From 04ab20b897111a8b119648ca7c2c76c628eca3bb Mon Sep 17 00:00:00 2001 From: Sebastian Toepfer <61313468+sebastian-toepfer@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:26:20 +0200 Subject: [PATCH 4/4] add support for exclusiveMaximum keyword --- core/pom.xml | 6 ++ .../core/vocab/basic/BasicVocabulary.java | 3 +- .../basic/ExclusiveMaximumKeywordType.java | 79 ++++++++++++++++ .../ExclusiveMaximumKeywordTypeTest.java | 94 +++++++++++++++++++ 4 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordType.java create mode 100644 core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordTypeTest.java diff --git a/core/pom.xml b/core/pom.xml index 97a1188f..7a4ae06b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -154,6 +154,12 @@ **/tests/draft2019-09/maximum.json **/tests/draft2020-12/maximum.json **/tests/draft-next/maximum.json + + **/tests/draft6/exclusiveMaximum.json + **/tests/draft7/exclusiveMaximum.json + **/tests/draft2019-09/exclusiveMaximum.json + **/tests/draft2020-12/exclusiveMaximum.json + **/tests/draft-next/exclusiveMaximum.json diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java index f00429ad..57cb0cad 100644 --- a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/BasicVocabulary.java @@ -44,7 +44,8 @@ public BasicVocabulary() { new PatternKeywordType(), new MinimumKeywordType(), new ExclusiveMinimumKeywordType(), - new MaximumKeywordType() + new MaximumKeywordType(), + new ExclusiveMaximumKeywordType() ); } diff --git a/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordType.java b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordType.java new file mode 100644 index 00000000..34ccc188 --- /dev/null +++ b/core/src/main/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordType.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright 2023 sebastian. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.github.sebastiantoepfer.jsonschema.core.vocab.basic; + +import io.github.sebastiantoepfer.jsonschema.ConstraintViolation; +import io.github.sebastiantoepfer.jsonschema.InstanceType; +import io.github.sebastiantoepfer.jsonschema.core.vocab.ConstraintAssertion; +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType; +import jakarta.json.JsonNumber; +import jakarta.json.JsonValue; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +final class ExclusiveMaximumKeywordType implements KeywordType { + + @Override + public String name() { + return "exclusiveMaximum"; + } + + @Override + public Keyword createKeyword(final JsonValue value) { + if (InstanceType.NUMBER.isInstance(value)) { + return new ExclusiveMaximumKeyword((JsonNumber) value); + } else { + throw new IllegalArgumentException("must be a number!"); + } + } + + private class ExclusiveMaximumKeyword implements ConstraintAssertion { + + private final BigDecimal max; + + private ExclusiveMaximumKeyword(final JsonNumber max) { + this.max = max.bigDecimalValue(); + } + + @Override + public boolean hasName(final String name) { + return Objects.equals(name(), name); + } + + @Override + public Collection violationsBy(final JsonValue value) { + final Collection result; + if (!InstanceType.NUMBER.isInstance(value) || max.compareTo(((JsonNumber) value).bigDecimalValue()) > 0) { + result = Collections.emptyList(); + } else { + result = List.of(new ConstraintViolation()); + } + return result; + } + } +} diff --git a/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordTypeTest.java b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordTypeTest.java new file mode 100644 index 00000000..b0d96614 --- /dev/null +++ b/core/src/test/java/io/github/sebastiantoepfer/jsonschema/core/vocab/basic/ExclusiveMaximumKeywordTypeTest.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.vocab.basic; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import io.github.sebastiantoepfer.jsonschema.keyword.Keyword; +import jakarta.json.Json; +import jakarta.json.JsonValue; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ExclusiveMaximumKeywordTypeTest { + + @Test + void should_know_his_name() { + final Keyword maximum = new ExclusiveMaximumKeywordType().createKeyword(Json.createValue(1)); + + assertThat(maximum.hasName("exclusiveMaximum"), is(true)); + assertThat(maximum.hasName("test"), is(false)); + } + + @Test + void should_not_becreatable_with_non_number() { + final ExclusiveMaximumKeywordType keywordType = new ExclusiveMaximumKeywordType(); + Assertions.assertThrows(IllegalArgumentException.class, () -> keywordType.createKeyword(JsonValue.FALSE)); + } + + @Test + void should_be_valid_for_non_number_values() { + assertThat( + new ExclusiveMaximumKeywordType() + .createKeyword(Json.createValue(1)) + .asAssertion() + .isValidFor(JsonValue.EMPTY_JSON_OBJECT), + is(true) + ); + } + + @Test + void should_be_invalid_for_greater_numbers() { + assertThat( + new ExclusiveMaximumKeywordType() + .createKeyword(Json.createValue(10)) + .asAssertion() + .isValidFor(Json.createValue(11)), + is(false) + ); + } + + @Test + void should_be_invalid_for_equals_numbers() { + assertThat( + new ExclusiveMaximumKeywordType() + .createKeyword(Json.createValue(10)) + .asAssertion() + .isValidFor(Json.createValue(10)), + is(false) + ); + } + + @Test + void shhould_be_valid_for_smaller_numbers() { + assertThat( + new ExclusiveMaximumKeywordType() + .createKeyword(Json.createValue(10)) + .asAssertion() + .isValidFor(Json.createValue(9)), + is(true) + ); + } +}