JOHNZON-171 more jsonschema validations - still not yet complete
Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/f4c41264 Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/f4c41264 Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/f4c41264 Branch: refs/heads/master Commit: f4c4126477fbbc9e201d48fb63b158515abfac3c Parents: 947c91b Author: Romain Manni-Bucau <[email protected]> Authored: Sun Apr 29 23:33:14 2018 +0200 Committer: Romain Manni-Bucau <[email protected]> Committed: Sun Apr 29 23:33:14 2018 +0200 ---------------------------------------------------------------------- .../johnzon/jsonschema/JsonSchemaValidator.java | 6 +- .../jsonschema/JsonSchemaValidatorFactory.java | 72 +++- .../jsonschema/spi/ValidationContext.java | 22 +- .../spi/builtin/BaseNumberValidation.java | 52 +++ .../spi/builtin/BaseNumberValidationImpl.java | 70 ---- .../jsonschema/spi/builtin/BaseValidation.java | 97 ++++++ .../spi/builtin/ContainsValidation.java | 78 +++++ .../jsonschema/spi/builtin/EnumValidation.java | 18 +- .../spi/builtin/ExclusiveMaximumValidation.java | 9 +- .../spi/builtin/ExclusiveMinimumValidation.java | 9 +- .../jsonschema/spi/builtin/ItemsValidation.java | 104 ++++++ .../spi/builtin/MaxItemsValidation.java | 69 ++++ .../spi/builtin/MaxLengthValidation.java | 27 +- .../spi/builtin/MaxPropertiesValidation.java | 69 ++++ .../spi/builtin/MaximumValidation.java | 9 +- .../spi/builtin/MinItemsValidation.java | 69 ++++ .../spi/builtin/MinLengthValidation.java | 27 +- .../spi/builtin/MinPropertiesValidation.java | 69 ++++ .../spi/builtin/MinimumValidation.java | 9 +- .../spi/builtin/MultipleOfValidation.java | 9 +- .../spi/builtin/PatternValidation.java | 23 +- .../spi/builtin/RequiredValidation.java | 22 +- .../jsonschema/spi/builtin/TypeValidation.java | 26 +- .../spi/builtin/UniqueItemsValidation.java | 67 ++++ .../jsonschema/JsonSchemaValidatorTest.java | 325 +++++++++++++++---- 25 files changed, 1084 insertions(+), 273 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java index f2f2372..78af768 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidator.java @@ -25,11 +25,11 @@ import java.util.Collection; import java.util.function.Function; import java.util.stream.Stream; -import javax.json.JsonObject; import javax.json.JsonValue; -public class JsonSchemaValidator implements Function<JsonObject, ValidationResult>, AutoCloseable { +public class JsonSchemaValidator implements Function<JsonValue, ValidationResult>, AutoCloseable { private static final ValidationResult SUCCESS = new ValidationResult(emptyList()); + private final Function<JsonValue, Stream<ValidationResult.ValidationError>> validationFunction; JsonSchemaValidator(final Function<JsonValue, Stream<ValidationResult.ValidationError>> validationFunction) { @@ -37,7 +37,7 @@ public class JsonSchemaValidator implements Function<JsonObject, ValidationResul } @Override - public ValidationResult apply(final JsonObject object) { + public ValidationResult apply(final JsonValue object) { final Collection<ValidationResult.ValidationError> errors = validationFunction.apply(object).collect(toList()); if (!errors.isEmpty()) { return new ValidationResult(errors); http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java index b409406..38880ba 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/JsonSchemaValidatorFactory.java @@ -35,17 +35,24 @@ import javax.json.JsonValue; import org.apache.johnzon.jsonschema.spi.ValidationContext; import org.apache.johnzon.jsonschema.spi.ValidationExtension; +import org.apache.johnzon.jsonschema.spi.builtin.ContainsValidation; import org.apache.johnzon.jsonschema.spi.builtin.EnumValidation; import org.apache.johnzon.jsonschema.spi.builtin.ExclusiveMaximumValidation; import org.apache.johnzon.jsonschema.spi.builtin.ExclusiveMinimumValidation; +import org.apache.johnzon.jsonschema.spi.builtin.ItemsValidation; +import org.apache.johnzon.jsonschema.spi.builtin.MaxItemsValidation; import org.apache.johnzon.jsonschema.spi.builtin.MaxLengthValidation; +import org.apache.johnzon.jsonschema.spi.builtin.MaxPropertiesValidation; import org.apache.johnzon.jsonschema.spi.builtin.MaximumValidation; +import org.apache.johnzon.jsonschema.spi.builtin.MinItemsValidation; import org.apache.johnzon.jsonschema.spi.builtin.MinLengthValidation; +import org.apache.johnzon.jsonschema.spi.builtin.MinPropertiesValidation; import org.apache.johnzon.jsonschema.spi.builtin.MinimumValidation; import org.apache.johnzon.jsonschema.spi.builtin.MultipleOfValidation; import org.apache.johnzon.jsonschema.spi.builtin.PatternValidation; import org.apache.johnzon.jsonschema.spi.builtin.RequiredValidation; import org.apache.johnzon.jsonschema.spi.builtin.TypeValidation; +import org.apache.johnzon.jsonschema.spi.builtin.UniqueItemsValidation; public class JsonSchemaValidatorFactory implements AutoCloseable { private static final String[] ROOT_PATH = new String[0]; @@ -75,7 +82,14 @@ public class JsonSchemaValidatorFactory implements AutoCloseable { new ExclusiveMinimumValidation(), new MaxLengthValidation(), new MinLengthValidation(), - new PatternValidation() + new PatternValidation(), + new ItemsValidation(this), + new MaxItemsValidation(), + new MinItemsValidation(), + new UniqueItemsValidation(), + new ContainsValidation(this), + new MaxPropertiesValidation(), + new MinPropertiesValidation() // todo: http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4 and following )); extensions.addAll(new ArrayList<>(StreamSupport.stream(ServiceLoader.load(ValidationExtension.class).spliterator(), false) @@ -93,7 +107,7 @@ public class JsonSchemaValidatorFactory implements AutoCloseable { } public JsonSchemaValidator newInstance(final JsonObject schema) { - return new JsonSchemaValidator(buildValidator(ROOT_PATH, schema)); + return new JsonSchemaValidator(buildValidator(ROOT_PATH, schema, null)); } @Override @@ -102,25 +116,34 @@ public class JsonSchemaValidatorFactory implements AutoCloseable { } private Function<JsonValue, Stream<ValidationResult.ValidationError>> buildValidator(final String[] path, - final JsonObject schema) { - final List<Function<JsonValue, Stream<ValidationResult.ValidationError>>> directValidations = buildDirectValidations(path, schema).collect(toList()); - final Function<JsonValue, Stream<ValidationResult.ValidationError>> nestedValidations = buildNestedValidations(path, schema); + final JsonObject schema, + final Function<JsonValue, JsonValue> valueProvider) { + final List<Function<JsonValue, Stream<ValidationResult.ValidationError>>> directValidations = buildDirectValidations(path, schema, valueProvider).collect(toList()); + final Function<JsonValue, Stream<ValidationResult.ValidationError>> nestedValidations = buildNestedValidations(path, schema, valueProvider); return new ValidationsFunction(Stream.concat(directValidations.stream(), Stream.of(nestedValidations)).collect(toList())); } - private Stream<Function<JsonValue, Stream<ValidationResult.ValidationError>>> buildDirectValidations(final String[] path, final JsonObject schema) { - final ValidationContext model = new ValidationContext(path, schema); - return extensions.stream().map(e -> e.create(model)).filter(Optional::isPresent).map(Optional::get); + private Stream<Function<JsonValue, Stream<ValidationResult.ValidationError>>> buildDirectValidations(final String[] path, + final JsonObject schema, + final Function<JsonValue, JsonValue> valueProvider) { + final ValidationContext model = new ValidationContext(path, schema, valueProvider); + return extensions.stream() + .map(e -> e.create(model)) + .filter(Optional::isPresent) + .map(Optional::get); } - private Function<JsonValue, Stream<ValidationResult.ValidationError>> buildNestedValidations(final String[] path, final JsonObject schema) { + private Function<JsonValue, Stream<ValidationResult.ValidationError>> buildNestedValidations(final String[] path, + final JsonObject schema, + final Function<JsonValue, JsonValue> valueProvider) { return ofNullable(schema.get("properties")) .filter(it -> it.getValueType() == JsonValue.ValueType.OBJECT) .map(it -> it.asJsonObject().entrySet().stream() .filter(obj -> obj.getValue().getValueType() == JsonValue.ValueType.OBJECT) .map(obj -> { - final String[] fieldPath = Stream.concat(Stream.of(path), Stream.of(obj.getKey())).toArray(String[]::new); - return buildValidator(fieldPath, obj.getValue().asJsonObject()); + final String key = obj.getKey(); + final String[] fieldPath = Stream.concat(Stream.of(path), Stream.of(key)).toArray(String[]::new); + return buildValidator(fieldPath, obj.getValue().asJsonObject(), new ChainedValueAccessor(valueProvider, key)); }) .collect(toList())) .map(this::toFunction) @@ -153,4 +176,31 @@ public class JsonSchemaValidatorFactory implements AutoCloseable { return delegates.toString(); } } + + private static class ChainedValueAccessor implements Function<JsonValue, JsonValue> { + private final Function<JsonValue, JsonValue> parent; + private final String key; + + private ChainedValueAccessor(final Function<JsonValue, JsonValue> valueProvider, final String key) { + this.parent = valueProvider; + this.key = key; + } + + @Override + public JsonValue apply(final JsonValue value) { + final JsonValue root = parent == null ? value : parent.apply(value); + if (root != null && root.getValueType() != JsonValue.ValueType.NULL && root.getValueType() == JsonValue.ValueType.OBJECT) { + return root.asJsonObject().get(key); + } + return JsonValue.NULL; + } + + @Override + public String toString() { + return "ChainedValueAccessor{" + + "parent=" + parent + + ", key='" + key + '\'' + + '}'; + } + } } http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java index 63535a5..04192cd 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/ValidationContext.java @@ -20,6 +20,7 @@ package org.apache.johnzon.jsonschema.spi; import static java.util.stream.Collectors.joining; +import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonObject; @@ -28,10 +29,16 @@ import javax.json.JsonValue; public class ValidationContext { private final String[] path; private final JsonObject schema; + private final Function<JsonValue, JsonValue> valueProvider; - public ValidationContext(final String[] path, final JsonObject schema) { + public ValidationContext(final String[] path, final JsonObject schema, final Function<JsonValue, JsonValue> valueProvider) { this.path = path; this.schema = schema; + this.valueProvider = valueProvider; + } + + public Function<JsonValue, JsonValue> getValueProvider() { + return valueProvider; } public String[] getPath() { @@ -42,19 +49,6 @@ public class ValidationContext { return schema; } - public JsonValue readValue(final JsonValue root) { // move to JsonPointer? requires to store a provider if we want - JsonValue current = root; - for (final String segment : path) { - if (current == null) { - return null; - } - if (current.getValueType() == JsonValue.ValueType.OBJECT) { - current = current.asJsonObject().get(segment); - } - } - return current; - } - public String toPointer() { return Stream.of(path).collect(joining("/", "/", "")); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidation.java new file mode 100644 index 0000000..35e920b --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidation.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonNumber; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; + +abstract class BaseNumberValidation extends BaseValidation { + protected final double bound; + + BaseNumberValidation(final String pointer, final Function<JsonValue, JsonValue> extractor, final double bound) { + super(pointer, extractor, JsonValue.ValueType.NUMBER); + this.bound = bound; + } + + @Override + protected Stream<ValidationResult.ValidationError> onNumber(final JsonNumber number) { + final double val = number.doubleValue(); + if (val <= 0) { + return toError(val); + } + if (isValid(val)) { + return Stream.empty(); + } + return toError(val); + } + + protected abstract boolean isValid(double val); + + protected abstract Stream<ValidationResult.ValidationError> toError(double val); +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java deleted file mode 100644 index e98e998..0000000 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseNumberValidationImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.johnzon.jsonschema.spi.builtin; - -import java.util.function.Function; -import java.util.stream.Stream; - -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonValue; - -import org.apache.johnzon.jsonschema.ValidationResult; - -abstract class BaseNumberValidationImpl implements Function<JsonValue, Stream<ValidationResult.ValidationError>> { - protected final String pointer; - protected final Function<JsonObject, JsonValue> extractor; - protected final double bound; - private final JsonValue.ValueType validType; - - BaseNumberValidationImpl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound, - final JsonValue.ValueType validType) { - this.bound = bound; - this.pointer = pointer; - this.extractor = extractor; - this.validType = validType; - } - - @Override - public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) { - if (obj == null || obj == JsonValue.NULL) { - return Stream.empty(); - } - final JsonValue value = extractor.apply(obj.asJsonObject()); - if (value == null || value.getValueType() != validType) { - return Stream.empty(); - } - final double val = toNumber(value); - if (val <= 0) { - return toError(val); - } - if (isValid(val)) { - return Stream.empty(); - } - return toError(val); - } - - protected double toNumber(final JsonValue value) { - return JsonNumber.class.cast(value).doubleValue(); - } - - protected abstract boolean isValid(double val); - - protected abstract Stream<ValidationResult.ValidationError> toError(double val); -} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseValidation.java new file mode 100644 index 0000000..5e0b9ed --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/BaseValidation.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; + +abstract class BaseValidation implements Function<JsonValue, Stream<ValidationResult.ValidationError>> { + protected final String pointer; + protected final Function<JsonValue, JsonValue> extractor; + private final JsonValue.ValueType validType; + private final boolean rootCanBeNull; + + BaseValidation(final String pointer, final Function<JsonValue, JsonValue> extractor, final JsonValue.ValueType validType) { + this.pointer = pointer; + this.extractor = extractor != null ? extractor : v -> v; + this.rootCanBeNull = extractor != null; + this.validType = validType; + } + + @Override + public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) { + if (isNull(obj) && rootCanBeNull) { + return Stream.empty(); + } + + final JsonValue value = extractor.apply(obj); + if (value == null || JsonValue.ValueType.NULL == value.getValueType() || value.getValueType() != validType) { + return Stream.empty(); + } + + switch (value.getValueType()) { + case STRING: + return onString(JsonString.class.cast(value)); + case TRUE: + case FALSE: + return onBoolean(JsonValue.TRUE.equals(value)); + case NUMBER: + return onNumber(JsonNumber.class.cast(value)); + case OBJECT: + return onObject(value.asJsonObject()); + case ARRAY: + return onArray(value.asJsonArray()); + case NULL: + return Stream.empty(); + } + throw new IllegalArgumentException("Unsupported value type: " + value); + } + + protected boolean isNull(final JsonValue obj) { + return null == obj || obj.getValueType() == JsonValue.ValueType.NULL; + } + + protected Stream<ValidationResult.ValidationError> onArray(final JsonArray array) { + return Stream.empty(); + } + + protected Stream<ValidationResult.ValidationError> onObject(final JsonObject object) { + return Stream.empty(); + } + + protected Stream<ValidationResult.ValidationError> onNumber(final JsonNumber number) { + return Stream.empty(); + } + + protected Stream<ValidationResult.ValidationError> onBoolean(final boolean value) { + return Stream.empty(); + } + + protected Stream<ValidationResult.ValidationError> onString(final JsonString cast) { + return Stream.empty(); + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ContainsValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ContainsValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ContainsValidation.java new file mode 100644 index 0000000..fe17da9 --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ContainsValidation.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.Collection; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonArray; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.JsonSchemaValidator; +import org.apache.johnzon.jsonschema.JsonSchemaValidatorFactory; +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class ContainsValidation implements ValidationExtension { + private final JsonSchemaValidatorFactory factory; + + public ContainsValidation(final JsonSchemaValidatorFactory factory) { + this.factory = factory; + } + + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("contains")) + .filter(it -> it.getValueType() == JsonValue.ValueType.OBJECT) + .map(it -> new ItemsValidator(model.toPointer(), model.getValueProvider(), factory.newInstance(it.asJsonObject()))); + } + + private static class ItemsValidator extends BaseValidation { + private final JsonSchemaValidator validator; + + private ItemsValidator(final String pointer, + final Function<JsonValue, JsonValue> extractor, + JsonSchemaValidator validator) { + super(pointer, extractor, JsonValue.ValueType.ARRAY); + this.validator = validator; + } + + @Override + protected Stream<ValidationResult.ValidationError> onArray(final JsonArray array) { + for (final JsonValue value : array) { + final Collection<ValidationResult.ValidationError> itemErrors = validator.apply(value).getErrors(); + if (itemErrors.isEmpty()) { + return Stream.empty(); + } + } + return Stream.of(new ValidationResult.ValidationError(pointer, "No item matching the expected schema")); + } + + @Override + public String toString() { + return "Contains{" + + "validator=" + validator + + ", pointer='" + pointer + '\'' + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java index 3cacbed..74cd4c4 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/EnumValidation.java @@ -25,7 +25,6 @@ import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -38,26 +37,23 @@ public class EnumValidation implements ValidationExtension { return ofNullable(model.getSchema().get("enum")) .filter(it -> it.getValueType() == JsonValue.ValueType.ARRAY) .map(JsonValue::asJsonArray) - .map(values -> new Impl(values, model.toPointer(), model::readValue)); + .map(values -> new Impl(values, model.getValueProvider(), model.toPointer())); } - private static class Impl implements Function<JsonValue, Stream<ValidationResult.ValidationError>> { + private static class Impl extends BaseValidation { private final Collection<JsonValue> valid; - private final String pointer; - private final Function<JsonObject, JsonValue> extractor; - private Impl(final Collection<JsonValue> valid, final String pointer, final Function<JsonObject, JsonValue> extractor) { + private Impl(final Collection<JsonValue> valid, final Function<JsonValue, JsonValue> extractor, final String pointer) { + super(pointer, extractor, JsonValue.ValueType.OBJECT /* ignored */); this.valid = valid; - this.pointer = pointer; - this.extractor = extractor; } @Override - public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) { - if (obj == null || obj == JsonValue.NULL) { + public Stream<ValidationResult.ValidationError> apply(final JsonValue root) { + if (isNull(root)) { return Stream.empty(); } - final JsonValue value = extractor.apply(obj.asJsonObject()); + final JsonValue value = extractor.apply(root); if (value != null && !JsonValue.NULL.equals(value)) { return Stream.empty(); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java index 2e06140..d5bbe50 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMaximumValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -36,14 +35,14 @@ public class ExclusiveMaximumValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("number")) { return Optional.ofNullable(model.getSchema().get("exclusiveMaximum")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).doubleValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).doubleValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound) { - super(pointer, extractor, bound, JsonValue.ValueType.NUMBER); + private static class Impl extends BaseNumberValidation { + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final double bound) { + super(pointer, valueProvider, bound); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java index a649476..aa47938 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ExclusiveMinimumValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -36,14 +35,14 @@ public class ExclusiveMinimumValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("number")) { return Optional.ofNullable(model.getSchema().get("exclusiveMinimum")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).doubleValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).doubleValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound) { - super(pointer, extractor, bound, JsonValue.ValueType.NUMBER); + private static class Impl extends BaseNumberValidation { + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final double bound) { + super(pointer, valueProvider, bound); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ItemsValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ItemsValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ItemsValidation.java new file mode 100644 index 0000000..3d7315b --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/ItemsValidation.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import static java.util.Collections.singleton; +import static java.util.stream.Collectors.toList; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonArray; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.JsonSchemaValidator; +import org.apache.johnzon.jsonschema.JsonSchemaValidatorFactory; +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class ItemsValidation implements ValidationExtension { + private final JsonSchemaValidatorFactory factory; + + public ItemsValidation(final JsonSchemaValidatorFactory factory) { + this.factory = factory; + } + + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("items")) + .map(items -> { + switch (items.getValueType()) { + case OBJECT: + final JsonSchemaValidator objectValidator = factory.newInstance(items.asJsonObject()); + return new ItemsValidator(model.toPointer(), model.getValueProvider(), singleton(objectValidator)); + case ARRAY: + return new ItemsValidator(model.toPointer(), model.getValueProvider(), items.asJsonArray().stream() + .filter(it -> it.getValueType() == JsonValue.ValueType.OBJECT) + .map(it -> factory.newInstance(it.asJsonObject())) + .collect(toList())); + default: + return null; + } + }); + } + + private static class ItemsValidator extends BaseValidation { + private final Collection<JsonSchemaValidator> objectValidators; + + private ItemsValidator(final String pointer, + final Function<JsonValue, JsonValue> extractor, + final Collection<JsonSchemaValidator> objectValidators) { + super(pointer, extractor, JsonValue.ValueType.ARRAY); + this.objectValidators = objectValidators; + } + + @Override + protected Stream<ValidationResult.ValidationError> onArray(final JsonArray array) { + Collection<ValidationResult.ValidationError> errors = null; + for (int i = 0; i < array.size(); i++) { + final JsonValue value = array.get(i); + final Collection<ValidationResult.ValidationError> itemErrors = objectValidators.stream() + .flatMap(validator -> validator.apply(value).getErrors().stream()) + .collect(toList()); + if (itemErrors != null && !itemErrors.isEmpty()) { + if (errors == null) { + errors = new ArrayList<>(); + } + final String suffix = "[" + i + "]"; + errors.addAll(itemErrors.stream() + .map(e -> new ValidationResult.ValidationError(pointer + e.getField() + suffix, e.getMessage())) + .collect(toList())); + } + } + return errors == null ? Stream.empty() : errors.stream(); + } + + @Override + public String toString() { + return "Items{" + + "validators=" + objectValidators + + ", pointer='" + pointer + '\'' + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxItemsValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxItemsValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxItemsValidation.java new file mode 100644 index 0000000..d60ec0d --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxItemsValidation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class MaxItemsValidation implements ValidationExtension { + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("maxItems")) + .filter(it -> it.getValueType() == JsonValue.ValueType.NUMBER) + .map(it -> JsonNumber.class.cast(it).intValue()) + .filter(it -> it >= 0) + .map(max -> new Impl(model.toPointer(), model.getValueProvider(), max)); + } + + private static class Impl extends BaseValidation { + private final int bound; + + private Impl(final String pointer, + final Function<JsonValue, JsonValue> extractor, + final int bound) { + super(pointer, extractor, JsonValue.ValueType.ARRAY); + this.bound = bound; + } + + @Override + protected Stream<ValidationResult.ValidationError> onArray(final JsonArray array) { + if (array.size() > bound) { + return Stream.of(new ValidationResult.ValidationError(pointer, "Too much items in the array (> " + bound + ")")); + } + return Stream.empty(); + } + + @Override + public String toString() { + return "MaxItems{" + + "max=" + bound + + ", pointer='" + pointer + '\'' + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java index 2049dbb..fe5a8c1 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxLengthValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonString; import javax.json.JsonValue; @@ -37,29 +36,25 @@ public class MaxLengthValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("string")) { return Optional.ofNullable(model.getSchema().get("maxLength")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).intValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).intValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound) { - super(pointer, extractor, bound, JsonValue.ValueType.STRING); - } + private static class Impl extends BaseValidation { + private final int bound; - @Override - protected double toNumber(final JsonValue value) { - return JsonString.class.cast(value).getString().length(); - } - - @Override - protected boolean isValid(final double val) { - return val <= this.bound; + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final int bound) { + super(pointer, valueProvider, JsonValue.ValueType.STRING); + this.bound = bound; } @Override - protected Stream<ValidationResult.ValidationError> toError(final double val) { - return Stream.of(new ValidationResult.ValidationError(pointer, val + " length is more than " + this.bound)); + protected Stream<ValidationResult.ValidationError> onString(final JsonString val) { + if (val.getString().length() > bound) { + return Stream.of(new ValidationResult.ValidationError(pointer, val + " length is more than " + this.bound)); + } + return Stream.empty(); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxPropertiesValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxPropertiesValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxPropertiesValidation.java new file mode 100644 index 0000000..36b06dd --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaxPropertiesValidation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class MaxPropertiesValidation implements ValidationExtension { + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("maxProperties")) + .filter(it -> it.getValueType() == JsonValue.ValueType.NUMBER) + .map(it -> JsonNumber.class.cast(it).intValue()) + .filter(it -> it >= 0) + .map(max -> new Impl(model.toPointer(), model.getValueProvider(), max)); + } + + private static class Impl extends BaseValidation { + private final int bound; + + private Impl(final String pointer, + final Function<JsonValue, JsonValue> extractor, + final int bound) { + super(pointer, extractor, JsonValue.ValueType.OBJECT); + this.bound = bound; + } + + @Override + protected Stream<ValidationResult.ValidationError> onObject(final JsonObject object) { + if (object.size() > bound) { + return Stream.of(new ValidationResult.ValidationError(pointer, "Too much properties (> " + bound + ")")); + } + return Stream.empty(); + } + + @Override + public String toString() { + return "MaxProperties{" + + "max=" + bound + + ", pointer='" + pointer + '\'' + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java index f0a6160..059eab8 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MaximumValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -36,14 +35,14 @@ public class MaximumValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("number")) { return Optional.ofNullable(model.getSchema().get("maximum")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).doubleValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).doubleValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound) { - super(pointer, extractor, bound, JsonValue.ValueType.NUMBER); + private static class Impl extends BaseNumberValidation { + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final double bound) { + super(pointer, valueProvider, bound); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinItemsValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinItemsValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinItemsValidation.java new file mode 100644 index 0000000..0787e2a --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinItemsValidation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class MinItemsValidation implements ValidationExtension { + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("minItems")) + .filter(it -> it.getValueType() == JsonValue.ValueType.NUMBER) + .map(it -> JsonNumber.class.cast(it).intValue()) + .filter(it -> it >= 0) + .map(max -> new Impl(model.toPointer(), model.getValueProvider(), max)); + } + + private static class Impl extends BaseValidation { + private final int bound; + + private Impl(final String pointer, + final Function<JsonValue, JsonValue> extractor, + final int bound) { + super(pointer, extractor, JsonValue.ValueType.ARRAY); + this.bound = bound; + } + + @Override + protected Stream<ValidationResult.ValidationError> onArray(final JsonArray array) { + if (array.size() < bound) { + return Stream.of(new ValidationResult.ValidationError(pointer, "Not enough items in the array (< " + bound + ")")); + } + return Stream.empty(); + } + + @Override + public String toString() { + return "MinItems{" + + "min=" + bound + + ", pointer='" + pointer + '\'' + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java index 54449a3..820e0ee 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinLengthValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonString; import javax.json.JsonValue; @@ -37,29 +36,25 @@ public class MinLengthValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("string")) { return Optional.ofNullable(model.getSchema().get("minLength")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).intValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).intValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound) { - super(pointer, extractor, bound, JsonValue.ValueType.STRING); - } + private static class Impl extends BaseValidation { + private final int bound; - @Override - protected double toNumber(final JsonValue value) { - return JsonString.class.cast(value).getString().length(); - } - - @Override - protected boolean isValid(final double val) { - return val >= this.bound; + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final int bound) { + super(pointer, valueProvider, JsonValue.ValueType.STRING); + this.bound = bound; } @Override - protected Stream<ValidationResult.ValidationError> toError(final double val) { - return Stream.of(new ValidationResult.ValidationError(pointer, val + " length is less than " + this.bound)); + protected Stream<ValidationResult.ValidationError> onString(final JsonString val) { + if (val.getString().length() < bound) { + return Stream.of(new ValidationResult.ValidationError(pointer, val + " length is less than " + this.bound)); + } + return Stream.empty(); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinPropertiesValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinPropertiesValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinPropertiesValidation.java new file mode 100644 index 0000000..ce8036d --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinPropertiesValidation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class MinPropertiesValidation implements ValidationExtension { + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("minProperties")) + .filter(it -> it.getValueType() == JsonValue.ValueType.NUMBER) + .map(it -> JsonNumber.class.cast(it).intValue()) + .filter(it -> it >= 0) + .map(max -> new Impl(model.toPointer(), model.getValueProvider(), max)); + } + + private static class Impl extends BaseValidation { + private final int bound; + + private Impl(final String pointer, + final Function<JsonValue, JsonValue> extractor, + final int bound) { + super(pointer, extractor, JsonValue.ValueType.OBJECT); + this.bound = bound; + } + + @Override + protected Stream<ValidationResult.ValidationError> onObject(final JsonObject object) { + if (object.size() < bound) { + return Stream.of(new ValidationResult.ValidationError(pointer, "Not enough properties (> " + bound + ")")); + } + return Stream.empty(); + } + + @Override + public String toString() { + return "MinProperties{" + + "min=" + bound + + ", pointer='" + pointer + '\'' + + '}'; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java index 1207111..4b33d67 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MinimumValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -36,14 +35,14 @@ public class MinimumValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("number")) { return Optional.ofNullable(model.getSchema().get("minimum")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).doubleValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).doubleValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double bound) { - super(pointer, extractor, bound, JsonValue.ValueType.NUMBER); + private static class Impl extends BaseNumberValidation { + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final double bound) { + super(pointer, valueProvider, bound); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java index 951dca8..a4c101c 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/MultipleOfValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.json.JsonNumber; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -36,14 +35,14 @@ public class MultipleOfValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("number")) { return Optional.ofNullable(model.getSchema().get("multipleOf")) .filter(v -> v.getValueType() == JsonValue.ValueType.NUMBER) - .map(m -> new Impl(model.toPointer(), model::readValue, JsonNumber.class.cast(m).doubleValue())); + .map(m -> new Impl(model.toPointer(), model.getValueProvider(), JsonNumber.class.cast(m).doubleValue())); } return Optional.empty(); } - private static class Impl extends BaseNumberValidationImpl { - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final double multipleOf) { - super(pointer, extractor, multipleOf, JsonValue.ValueType.NUMBER); + private static class Impl extends BaseNumberValidation { + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final double multipleOf) { + super(pointer, valueProvider, multipleOf); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java index 4b4d284..7085225 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/PatternValidation.java @@ -23,7 +23,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; -import javax.json.JsonObject; import javax.json.JsonString; import javax.json.JsonValue; import javax.script.Bindings; @@ -41,32 +40,22 @@ public class PatternValidation implements ValidationExtension { if (model.getSchema().getString("type", "object").equals("string")) { return Optional.ofNullable(model.getSchema().get("pattern")) .filter(val -> val.getValueType() == JsonValue.ValueType.STRING) - .map(pattern -> new Impl(model.toPointer(), model::readValue, JsonString.class.cast(pattern).getString())); + .map(pattern -> new Impl(model.toPointer(), model.getValueProvider(), JsonString.class.cast(pattern).getString())); } return Optional.empty(); } - private static class Impl implements Function<JsonValue, Stream<ValidationResult.ValidationError>> { - private final String pointer; - private final Function<JsonObject, JsonValue> extractor; + private static class Impl extends BaseValidation { private final JsRegex jsRegex; - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final String pattern) { + private Impl(final String pointer, final Function<JsonValue, JsonValue> valueProvider, final String pattern) { + super(pointer, valueProvider, JsonValue.ValueType.STRING); this.jsRegex = new JsRegex(pattern); - this.pointer = pointer; - this.extractor = extractor; } @Override - public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) { - if (obj == null || obj == JsonValue.NULL) { - return Stream.empty(); - } - final JsonValue value = extractor.apply(obj.asJsonObject()); - if (value == null || value.getValueType() != JsonValue.ValueType.STRING || JsonValue.NULL.equals(value)) { - return Stream.empty(); - } - if (!jsRegex.test(JsonString.class.cast(value).getString())) { + public Stream<ValidationResult.ValidationError> onString(final JsonString value) { + if (!jsRegex.test(value.getString())) { return Stream.of(new ValidationResult.ValidationError(pointer, value + " doesn't match " + jsRegex)); } return Stream.empty(); http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java index cc547b1..2749162 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/RequiredValidation.java @@ -42,33 +42,23 @@ public class RequiredValidation implements ValidationExtension { .map(JsonValue::asJsonArray) .filter(arr -> arr.stream().allMatch(it -> it.getValueType() == JsonValue.ValueType.STRING)) .map(arr -> arr.stream().map(it -> JsonString.class.cast(it).getString()).collect(toSet())) - .map(required -> new Impl(required, model.toPointer(), model::readValue)); + .map(required -> new Impl(required, model.getValueProvider(), model.toPointer())); } - private static class Impl implements Function<JsonValue, Stream<ValidationResult.ValidationError>> { + private static class Impl extends BaseValidation { private final Collection<String> required; - private final String pointer; - private final Function<JsonObject, JsonValue> extractor; - private Impl(final Collection<String> required, final String pointer, final Function<JsonObject, JsonValue> extractor) { + private Impl(final Collection<String> required, final Function<JsonValue, JsonValue> extractor, final String pointer) { + super(pointer, extractor, JsonValue.ValueType.OBJECT); this.required = required; - this.pointer = pointer; - this.extractor = extractor; } @Override - public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) { + public Stream<ValidationResult.ValidationError> onObject(final JsonObject obj) { if (obj == null || obj == JsonValue.NULL) { return toErrors(required.stream()); } - return toErrors(required.stream().filter(name -> { - final JsonValue jsonValue = extractor.apply(obj.asJsonObject()); - return isNull(jsonValue) || isNull(jsonValue.asJsonObject().get(name)); - })); - } - - private boolean isNull(final JsonValue jsonValue) { - return jsonValue == null || JsonValue.NULL.equals(jsonValue); + return toErrors(required.stream().filter(name -> isNull(obj.get(name)))); } private Stream<ValidationResult.ValidationError> toErrors(final Stream<String> fields) { http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java index 35122aa..b99685f 100644 --- a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/TypeValidation.java @@ -25,7 +25,6 @@ import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; -import javax.json.JsonObject; import javax.json.JsonValue; import org.apache.johnzon.jsonschema.ValidationResult; @@ -37,36 +36,33 @@ public class TypeValidation implements ValidationExtension { public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { switch (model.getSchema().getString("type", "object")) { case "string": - return Optional.of(new Impl(model.toPointer(), model::readValue, JsonValue.ValueType.STRING)); + return Optional.of(new Impl(model.toPointer(), model.getValueProvider(), JsonValue.ValueType.STRING)); case "number": - return Optional.of(new Impl(model.toPointer(), model::readValue, JsonValue.ValueType.NUMBER)); + return Optional.of(new Impl(model.toPointer(), model.getValueProvider(), JsonValue.ValueType.NUMBER)); case "array": - return Optional.of(new Impl(model.toPointer(), model::readValue, JsonValue.ValueType.ARRAY)); + return Optional.of(new Impl(model.toPointer(), model.getValueProvider(), JsonValue.ValueType.ARRAY)); case "boolean": - return Optional.of(new Impl(model.toPointer(), model::readValue, JsonValue.ValueType.FALSE, JsonValue.ValueType.TRUE)); + return Optional.of(new Impl(model.toPointer(), model.getValueProvider(), JsonValue.ValueType.FALSE, JsonValue.ValueType.TRUE)); case "object": default: - return Optional.of(new Impl(model.toPointer(), model::readValue, JsonValue.ValueType.OBJECT)); + return Optional.of(new Impl(model.toPointer(), model.getValueProvider(), JsonValue.ValueType.OBJECT)); } } - private static class Impl implements Function<JsonValue, Stream<ValidationResult.ValidationError>> { - private final String pointer; - private final Function<JsonObject, JsonValue> extractor; + private static class Impl extends BaseValidation { private final JsonValue.ValueType[] types; - private Impl(final String pointer, final Function<JsonObject, JsonValue> extractor, final JsonValue.ValueType... types) { + private Impl(final String pointer, final Function<JsonValue, JsonValue> extractor, final JsonValue.ValueType... types) { + super(pointer, extractor, JsonValue.ValueType.OBJECT /*ignored*/); this.types = types; - this.pointer = pointer; - this.extractor = extractor; } @Override - public Stream<ValidationResult.ValidationError> apply(final JsonValue obj) { - if (obj == null || obj == JsonValue.NULL) { + public Stream<ValidationResult.ValidationError> apply(final JsonValue root) { + if (isNull(root)) { return Stream.empty(); } - final JsonValue value = extractor.apply(obj.asJsonObject()); + final JsonValue value = extractor.apply(root); if (value == null || Stream.of(types).anyMatch(it -> it == value.getValueType()) || JsonValue.ValueType.NULL == value.getValueType()) { return Stream.empty(); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/f4c41264/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/UniqueItemsValidation.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/UniqueItemsValidation.java b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/UniqueItemsValidation.java new file mode 100644 index 0000000..ceef0b6 --- /dev/null +++ b/johnzon-jsonschema/src/main/java/org/apache/johnzon/jsonschema/spi/builtin/UniqueItemsValidation.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonschema.spi.builtin; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.json.JsonArray; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonschema.ValidationResult; +import org.apache.johnzon.jsonschema.spi.ValidationContext; +import org.apache.johnzon.jsonschema.spi.ValidationExtension; + +public class UniqueItemsValidation implements ValidationExtension { + @Override + public Optional<Function<JsonValue, Stream<ValidationResult.ValidationError>>> create(final ValidationContext model) { + return Optional.ofNullable(model.getSchema().get("uniqueItems")) + .filter(it -> it.getValueType() == JsonValue.ValueType.TRUE) + .map(max -> new Impl(model.toPointer(), model.getValueProvider())); + } + + private static class Impl extends BaseValidation { + private Impl(final String pointer, + final Function<JsonValue, JsonValue> extractor) { + super(pointer, extractor, JsonValue.ValueType.ARRAY); + } + + @Override + protected Stream<ValidationResult.ValidationError> onArray(final JsonArray array) { + final Collection<JsonValue> uniques = new HashSet<>(array); + if (array.size() != uniques.size()) { + final Collection<JsonValue> duplicated = new ArrayList<>(array); + duplicated.removeAll(uniques); + return Stream.of(new ValidationResult.ValidationError(pointer, "duplicated items: " + duplicated)); + } + return Stream.empty(); + } + + @Override + public String toString() { + return "UniqueItems{" + + "pointer='" + pointer + '\'' + + '}'; + } + } +}
