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)
+ );
+ }
+}