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 + '\'' +
+                    '}';
+        }
+    }
+}

Reply via email to