> INTRINSIC_TYPES = unmodifiableList(asList(
Number.class,
String.class,
- Boolean.class,
- JSONObject.class,
- JSONArray.class,
- JSONObject.NULL.getClass()
+ Boolean.class
));
- static final String TYPE_FAILURE_MSG = "subject is an instance of non-handled type %s. Should be one of "
- + VALIDATED_TYPES.stream().map(Class::getSimpleName).collect(joining(", "));
-
- private static boolean isNull(Object obj) {
- return obj == null || JSONObject.NULL.equals(obj);
- }
+ static final String TYPE_FAILURE_MSG = "subject is an instance of non-handled type %s. Should be one of %s.";
protected Object subject;
@@ -37,6 +29,12 @@ private static boolean isNull(Object obj) {
private final ReadWriteValidator readWriteValidator;
+ private final JsonAdaptation> jsonAdaptation;
+
+ private boolean isNull(Object obj) {
+ return obj == null || jsonAdaptation.isNull(obj);
+ }
+
@Override
void visit(Schema schema) {
if (schema.isNullable() == Boolean.FALSE && isNull(subject)) {
@@ -46,23 +44,29 @@ void visit(Schema schema) {
super.visit(schema);
}
- ValidatingVisitor(Object subject, ValidationFailureReporter failureReporter, ReadWriteValidator readWriteValidator) {
- if (subject != null && !VALIDATED_TYPES.stream().anyMatch(type -> type.isAssignableFrom(subject.getClass()))) {
- throw new IllegalArgumentException(format(TYPE_FAILURE_MSG, subject.getClass().getSimpleName()));
+ ValidatingVisitor(Object subject, ValidationFailureReporter failureReporter,
+ ReadWriteValidator readWriteValidator, JsonAdaptation> jsonAdaptation) {
+ if (subject != null
+ && !INTRINSIC_TYPES.stream().anyMatch(type -> type.isAssignableFrom(subject.getClass()))
+ && !jsonAdaptation.isSupportedType(subject.getClass())) {
+ throw new IllegalArgumentException(format(TYPE_FAILURE_MSG, subject.getClass().getSimpleName(),
+ Stream.concat(INTRINSIC_TYPES.stream(), Stream.of(jsonAdaptation.supportedTypes()))
+ .map(Class::getSimpleName).collect(joining(", "))));
}
this.subject = subject;
+ this.jsonAdaptation = jsonAdaptation;
this.failureReporter = failureReporter;
this.readWriteValidator = readWriteValidator;
}
@Override
void visitNumberSchema(NumberSchema numberSchema) {
- numberSchema.accept(new NumberSchemaValidatingVisitor(subject, this));
+ numberSchema.accept(new NumberSchemaValidatingVisitor(subject, this, jsonAdaptation));
}
@Override
void visitArraySchema(ArraySchema arraySchema) {
- arraySchema.accept(new ArraySchemaValidatingVisitor(subject, this));
+ arraySchema.accept(new ArraySchemaValidatingVisitor(subject, this, jsonAdaptation));
}
@Override
@@ -74,7 +78,7 @@ void visitBooleanSchema(BooleanSchema schema) {
@Override
void visitNullSchema(NullSchema nullSchema) {
- if (!(subject == null || subject == JSONObject.NULL)) {
+ if (!(subject == null || jsonAdaptation.isNull(subject))) {
failureReporter.failure("expected: null, found: " + subject.getClass().getSimpleName(), "type");
}
}
@@ -84,7 +88,7 @@ void visitConstSchema(ConstSchema constSchema) {
if (isNull(subject) && isNull(constSchema.getPermittedValue())) {
return;
}
- Object effectiveSubject = toJavaValue(subject);
+ Object effectiveSubject = toJavaValue(jsonAdaptation.adapt(subject));
if (!ObjectComparator.deepEquals(effectiveSubject, constSchema.getPermittedValue())) {
failureReporter.failure("", "const");
}
@@ -92,7 +96,7 @@ void visitConstSchema(ConstSchema constSchema) {
@Override
void visitEnumSchema(EnumSchema enumSchema) {
- Object effectiveSubject = toJavaValue(subject);
+ Object effectiveSubject = toJavaValue(jsonAdaptation.adapt(subject));
for (Object possibleValue : enumSchema.getPossibleValues()) {
if (ObjectComparator.deepEquals(possibleValue, effectiveSubject)) {
return;
@@ -129,12 +133,12 @@ void visitReferenceSchema(ReferenceSchema referenceSchema) {
@Override
void visitObjectSchema(ObjectSchema objectSchema) {
- objectSchema.accept(new ObjectSchemaValidatingVisitor(subject, this));
+ objectSchema.accept(new ObjectSchemaValidatingVisitor(subject, this, jsonAdaptation));
}
@Override
void visitStringSchema(StringSchema stringSchema) {
- stringSchema.accept(new StringSchemaValidatingVisitor(subject, this));
+ stringSchema.accept(new StringSchemaValidatingVisitor(subject, this, jsonAdaptation));
}
@Override
@@ -168,7 +172,9 @@ void visitConditionalSchema(ConditionalSchema conditionalSchema) {
ValidationException getFailureOfSchema(Schema schema, Object input) {
Object origSubject = this.subject;
- this.subject = input;
+ // TODO: performance could be improved by revisiting test cases that break when
+ // the adaptation value is not inverted here
+ this.subject = jsonAdaptation.invert(input);
ValidationException rval = failureReporter.inContextOfSchema(schema, () -> visit(schema));
this.subject = origSubject;
return rval;
diff --git a/core/src/main/java/org/everit/json/schema/Validator.java b/core/src/main/java/org/everit/json/schema/Validator.java
index b780b8995..5238e206c 100644
--- a/core/src/main/java/org/everit/json/schema/Validator.java
+++ b/core/src/main/java/org/everit/json/schema/Validator.java
@@ -1,5 +1,7 @@
package org.everit.json.schema;
+import org.everit.json.schema.spi.JsonAdaptation;
+
import java.util.function.BiFunction;
public interface Validator {
@@ -10,6 +12,8 @@ class ValidatorBuilder {
private ReadWriteContext readWriteContext;
+ private JsonAdaptation jsonAdaptation = new JSONAdaptation();
+
public ValidatorBuilder failEarly() {
this.failEarly = true;
return this;
@@ -20,8 +24,13 @@ public ValidatorBuilder readWriteContext(ReadWriteContext readWriteContext) {
return this;
}
+ public ValidatorBuilder jsonAdaptation(JsonAdaptation jsonAdaptation) {
+ this.jsonAdaptation = jsonAdaptation;
+ return this;
+ }
+
public Validator build() {
- return new DefaultValidator(failEarly, readWriteContext);
+ return new DefaultValidator(failEarly, readWriteContext, jsonAdaptation);
}
}
@@ -41,15 +50,18 @@ class DefaultValidator implements Validator {
private final ReadWriteContext readWriteContext;
- DefaultValidator(boolean failEarly, ReadWriteContext readWriteContext) {
+ private final JsonAdaptation jsonAdaptation;
+
+ DefaultValidator(boolean failEarly, ReadWriteContext readWriteContext, JsonAdaptation jsonAdaptation) {
this.failEarly = failEarly;
this.readWriteContext = readWriteContext;
+ this.jsonAdaptation = jsonAdaptation;
}
@Override public void performValidation(Schema schema, Object input) {
ValidationFailureReporter failureReporter = createFailureReporter(schema);
ReadWriteValidator readWriteValidator = ReadWriteValidator.createForContext(readWriteContext, failureReporter);
- ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter, readWriteValidator);
+ ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter, readWriteValidator, jsonAdaptation);
visitor.visit(schema);
visitor.failIfErrorFound();
}
diff --git a/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonAdaptation.java b/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonAdaptation.java
new file mode 100644
index 000000000..9065f32fe
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonAdaptation.java
@@ -0,0 +1,235 @@
+package org.everit.json.schema.javax.json;
+
+import org.everit.json.schema.spi.JsonAdaptation;
+import org.everit.json.schema.spi.JsonAdapter;
+
+import javax.json.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A {@link JsonAdaptation} for the standard Java JSON types in the {@code javax.json}
+ * package.
+ *
+ * The current specification for these types is part of the JSON-P specification
+ * (JSR-374), which is part of the Java EE 8 platform. An earlier version of the JSON-P
+ * specification (JSR-353) was used in the Java EE 7 platform.
+ *
+ * Because the later specification provides some improvements to the API needed to support
+ * these types with JSON schema validation, we want to use the later version where possible.
+ * However, most Java EE 7 containers will have the older JSR-353 API.
+ *
+ * Using the {@link #newInstance()} method, a user of this adaptation can get an instance
+ * that uses the latest version of the JSON-P that is available at runtime.
+ */
+public abstract class JavaxJsonAdaptation implements JsonAdaptation {
+
+ static final String JSR_374_ADAPTATION = "Jsr374Adaptation";
+ static final String JSR_353_ADAPTATION = "Jsr353Adaptation";
+
+ @Override
+ public Class> arrayType() {
+ return JsonArray.class;
+ }
+
+ @Override
+ public Class> objectType() {
+ return JsonObject.class;
+ }
+
+ @Override
+ public Class>[] supportedTypes() {
+ return new Class>[] { JsonValue.class };
+ }
+
+ /**
+ * Creates a new instance of an adaptation for the standard JSON types using the
+ * latest version of the JSON-P implementation that is available via either the
+ * thread context class loader (if one is set on the calling thread) or from the
+ * same class loader that loaded this class.
+ *
+ * @return adaptation instance for JSON-P data types
+ * @throws RuntimeException if the adaptation class cannot be instantiated
+ */
+ public static JavaxJsonAdaptation newInstance() {
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ return newInstance(Thread.currentThread().getContextClassLoader());
+ }
+ return newInstance(JavaxJsonAdaptation.class.getClassLoader());
+ }
+
+ /**
+ * Creates a new instance of an adaptation for the standard JSON types using the
+ * latest version of the JSON-P implementation that is available on the specified
+ * class loader.
+ *
+ * This method may be use in dynamic modular runtime environments such as those
+ * provided by OSGi.
+ *
+ * @param classLoader the class loader to use to find the JSON-P API and
+ * implementation classes
+ * @return adaptation instance for JSON-P data types
+ * @throws RuntimeException if the adaptation class cannot be instantiated
+ */
+ public static JavaxJsonAdaptation newInstance(ClassLoader classLoader) {
+ return newInstance(classLoader, determineProvider("createValue", String.class));
+ }
+
+ /**
+ * Creates a new adaptation instance using the given class loader to load a
+ * specific provider name.
+ * @param classLoader source class loader for the JSON-P types
+ * @param providerName name of the {@link JavaxJsonAdaptation} to load
+ * @return adaptation instance
+ * @throws RuntimeException an instance of the specified type cannot be instantiated
+ */
+ static JavaxJsonAdaptation newInstance(ClassLoader classLoader,
+ String providerName) {
+ try {
+ return (JavaxJsonAdaptation) classLoader.loadClass(
+ providerClassName(providerName)).newInstance();
+ }
+ catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Determine the name of the adaptation provider class based on the availability
+ * of a particular sentinel method in the {@link Json} class.
+ * @param sentinelMethodName name of the method whose presence is to be checked
+ * @param argTypes argument types for the sentinel method
+ * @return adaptation provider name
+ */
+ static String determineProvider(String sentinelMethodName, Class>... argTypes) {
+ try {
+ Json.class.getMethod(sentinelMethodName, argTypes);
+ return JSR_374_ADAPTATION;
+ } catch (NoSuchMethodException ex) {
+ return JSR_353_ADAPTATION;
+ }
+ }
+
+ /**
+ * Constructs the fully-qualified class name for a given named provider class.
+ *
+ * It is assumed that all providers are subtypes of this abstract base type and
+ * are located in the same package.
+ *
+ * @param providerName provider class name
+ * @return fully qualified class name
+ */
+ private static String providerClassName(String providerName) {
+ return JavaxJsonAdaptation.class.getPackage().getName() + "." + providerName;
+ }
+
+ @Override
+ public boolean isSupportedType(Class> type) {
+ return JsonValue.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public boolean isNull(Object value) {
+ return value == null || value == JsonValue.NULL;
+ }
+
+ @Override
+ public Object adapt(Object value) {
+ if (value == JsonValue.NULL) {
+ return null;
+ } else if (value == JsonValue.TRUE) {
+ return true;
+ } else if (value == JsonValue.FALSE) {
+ return false;
+ } else if (value instanceof JsonString) {
+ return ((JsonString) value).getString();
+ } else if (value instanceof JsonNumber) {
+ if (((JsonNumber) value).isIntegral()) {
+ return ((JsonNumber) value).bigIntegerValue();
+ } else {
+ return ((JsonNumber) value).bigDecimalValue();
+ }
+ } else if (value instanceof JsonArray) {
+ return new JavaxJsonArrayAdapter((JsonArray) value);
+ } else if (value instanceof JsonObject) {
+ return new JavaxJsonObjectAdapter((JsonObject) value);
+ } else {
+ return value;
+ }
+ }
+
+ @Override
+ public JsonValue invert(Object value) {
+ if (value == null) {
+ return JsonValue.NULL;
+ }
+ if (value instanceof JsonAdapter) {
+ return (JsonValue) ((JsonAdapter) value).unwrap();
+ } else if (value instanceof String) {
+ return createValue((String) value);
+ } else if (value instanceof Boolean) {
+ return ((boolean) value) ? JsonValue.TRUE : JsonValue.FALSE;
+ } else if (value instanceof Integer || value instanceof Byte || value instanceof Short) {
+ return createValue((int) value);
+ } else if (value instanceof Long) {
+ return createValue((long) value);
+ } else if (value instanceof Double || value instanceof Float) {
+ return createValue((double) value);
+ } else if (value instanceof BigInteger) {
+ return createValue((BigInteger) value);
+ } else if (value instanceof BigDecimal) {
+ return createValue((BigDecimal) value);
+ } else {
+ throw new AssertionError("unrecognized intrinsic type");
+ }
+ }
+
+ /**
+ * Creates a {@link JsonValue} representing the given string using a provider-specific
+ * mechanism.
+ * @param value the subject string value
+ * @return JSON value instance
+ */
+ abstract JsonValue createValue(String value);
+
+ /**
+ * Creates a {@link JsonValue} representing the given integer using a provider-specific
+ * mechanism.
+ * @param value the subject integer value
+ * @return JSON value instance
+ */
+ abstract JsonValue createValue(int value);
+
+ /**
+ * Creates a {@link JsonValue} representing the given long using a provider-specific
+ * mechanism.
+ * @param value the subject long value
+ * @return JSON value instance
+ */
+ abstract JsonValue createValue(long value);
+
+ /**
+ * Creates a {@link JsonValue} representing the given double using a provider-specific
+ * mechanism.
+ * @param value the subject double value
+ * @return JSON value instance
+ */
+ abstract JsonValue createValue(double value);
+
+ /**
+ * Creates a {@link JsonValue} representing the given big integer using a
+ * provider-specific mechanism.
+ * @param value the subject big integer value
+ * @return JSON value instance
+ */
+ abstract JsonValue createValue(BigInteger value);
+
+ /**
+ * Creates a {@link JsonValue} representing the given big decimal using a
+ * provider-specific mechanism.
+ * @param value the subject big decimal value
+ * @return JSON value instance
+ */
+ abstract JsonValue createValue(BigDecimal value);
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonArrayAdapter.java b/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonArrayAdapter.java
new file mode 100644
index 000000000..31f9851d6
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonArrayAdapter.java
@@ -0,0 +1,46 @@
+package org.everit.json.schema.javax.json;
+
+import org.everit.json.schema.spi.JsonArrayAdapter;
+
+import javax.json.JsonArray;
+import javax.json.JsonValue;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link JsonArrayAdapter} that delegates to a JSON-P {@link JsonArray}.
+ */
+class JavaxJsonArrayAdapter implements JsonArrayAdapter {
+
+ private final JsonArray delegate;
+
+ JavaxJsonArrayAdapter(JsonArray delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int length() {
+ return delegate.size();
+ }
+
+ @Override
+ public JsonValue get(int index) {
+ return delegate.get(index);
+ }
+
+ @Override
+ public void put(int index, JsonValue value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List toList() {
+ return Collections.unmodifiableList(delegate);
+ }
+
+ @Override
+ public JsonValue unwrap() {
+ return delegate;
+ }
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonObjectAdapter.java b/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonObjectAdapter.java
new file mode 100644
index 000000000..75a79b6f0
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/javax/json/JavaxJsonObjectAdapter.java
@@ -0,0 +1,56 @@
+package org.everit.json.schema.javax.json;
+
+import org.everit.json.schema.spi.JsonObjectAdapter;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * A {@link JsonObjectAdapter} that delegates to a JSON-P {@link JsonObject}.
+ */
+class JavaxJsonObjectAdapter implements JsonObjectAdapter {
+
+ private final JsonObject delegate;
+
+ JavaxJsonObjectAdapter(JsonObject delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int length() {
+ return delegate.size();
+ }
+
+ @Override
+ public String[] keys() {
+ return delegate.keySet().toArray(new String[0]);
+ }
+
+ @Override
+ public boolean has(String key) {
+ return delegate.containsKey(key);
+ }
+
+ @Override
+ public JsonValue get(String key) {
+ return delegate.get(key);
+ }
+
+ @Override
+ public void put(String key, JsonValue value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map toMap() {
+ return Collections.unmodifiableMap(delegate);
+ }
+
+ @Override
+ public JsonValue unwrap() {
+ return delegate;
+ }
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/javax/json/Jsr353Adaptation.java b/core/src/main/java/org/everit/json/schema/javax/json/Jsr353Adaptation.java
new file mode 100644
index 000000000..734b5e7fc
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/javax/json/Jsr353Adaptation.java
@@ -0,0 +1,53 @@
+package org.everit.json.schema.javax.json;
+
+import javax.json.Json;
+import javax.json.JsonValue;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * An adaptation for the JSON types of JSR-353 for support of Java EE 7.
+ *
+ * In JSR-353, none of the {@link Json} methods to create JSON scalar
+ * values are available. However there are methods to create array
+ * and object builders. To create a single scalar value, in this
+ * implementation we use an array builder; adding an intrinsic scalar
+ * value and then extracting it from the completed array.
+ *
+ * While this approach has some additional overhead, it at least provides
+ * a means to support Java EE 7 which includes the JSR-353 specification
+ * of the standard JSON types.
+ */
+public class Jsr353Adaptation extends JavaxJsonAdaptation {
+
+ @Override
+ JsonValue createValue(String value) {
+ return Json.createArrayBuilder().add(value).build().get(0);
+ }
+
+ @Override
+ JsonValue createValue(int value) {
+ return Json.createArrayBuilder().add(value).build().get(0);
+ }
+
+ @Override
+ JsonValue createValue(double value) {
+ return Json.createArrayBuilder().add(value).build().get(0);
+ }
+
+ @Override
+ JsonValue createValue(long value) {
+ return Json.createArrayBuilder().add(value).build().get(0);
+ }
+
+ @Override
+ JsonValue createValue(BigInteger value) {
+ return Json.createArrayBuilder().add(value).build().get(0);
+ }
+
+ @Override
+ JsonValue createValue(BigDecimal value) {
+ return Json.createArrayBuilder().add(value).build().get(0);
+ }
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/javax/json/Jsr374Adaptation.java b/core/src/main/java/org/everit/json/schema/javax/json/Jsr374Adaptation.java
new file mode 100644
index 000000000..8ca90c2d6
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/javax/json/Jsr374Adaptation.java
@@ -0,0 +1,43 @@
+package org.everit.json.schema.javax.json;
+
+import javax.json.Json;
+import javax.json.JsonValue;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * An adaptation for the JSON types of JSR-374 for support of Java EE 8.
+ */
+public class Jsr374Adaptation extends JavaxJsonAdaptation {
+
+ @Override
+ JsonValue createValue(String value) {
+ return Json.createValue(value);
+ }
+
+ @Override
+ JsonValue createValue(int value) {
+ return Json.createValue(value);
+ }
+
+ @Override
+ JsonValue createValue(double value) {
+ return Json.createValue(value);
+ }
+
+ @Override
+ JsonValue createValue(long value) {
+ return Json.createValue(value);
+ }
+
+ @Override
+ JsonValue createValue(BigInteger value) {
+ return Json.createValue(value);
+ }
+
+ @Override
+ JsonValue createValue(BigDecimal value) {
+ return Json.createValue(value);
+ }
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/javax/json/package-info.java b/core/src/main/java/org/everit/json/schema/javax/json/package-info.java
new file mode 100644
index 000000000..8f9b263fb
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/javax/json/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Adaptation for the JSON-P types of {@code javax.json}
+ */
+package org.everit.json.schema.javax.json;
\ No newline at end of file
diff --git a/core/src/main/java/org/everit/json/schema/spi/JsonAdaptation.java b/core/src/main/java/org/everit/json/schema/spi/JsonAdaptation.java
new file mode 100644
index 000000000..8ab537a11
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/spi/JsonAdaptation.java
@@ -0,0 +1,82 @@
+package org.everit.json.schema.spi;
+
+/**
+ * A service that adapts JSON types for use in schema validation.
+ *
+ * An adaption is responsible for adapting implementation-specific JSON object and array
+ * structures to instances of the {@link JsonObjectAdapter} and {@link JsonArrayAdapter} types.
+ * JSON scalar values are adapted to an intrinsic type (String, Boolean, Number).
+ *
+ * An adaptation can assume that for a given validation operation, exactly one
+ * adaptation is in use, and that any given {@link JsonAdapter} was produced by
+ * the single adaptation that is in use.
+ *
+ * @param the base type common to the types used in the underlying JSON implementation
+ */
+public interface JsonAdaptation {
+
+ /**
+ * Gets the implementation-specific type that represents a JSON array.
+ * @return array type
+ */
+ Class> arrayType();
+
+ /**
+ * Gets the implementation-specific type that represents a JSON object.
+ * @return object type
+ */
+ Class> objectType();
+
+ /**
+ * Gets the types supported by this implementation.
+ * @return array of supported types
+ */
+ Class>[] supportedTypes();
+
+ /**
+ * Given an arbitrary type, tests whether the type is recognized as an adaptable type
+ * in this adaptation.
+ *
+ * An implementation does not need to recognize the intrinsic types (String, Boolean, Number)
+ * but must recognize its own types.
+ *
+ * @param type the subject type to test
+ * @return {@code true} if {@code type} is an adaptable type
+ */
+ boolean isSupportedType(Class> type);
+
+ /**
+ * Given an arbitrary value, tests whether the value is logically equal to null in this
+ * adaptation.
+ * @param value the value to test.
+ * @return {@code true} if {@code value == null} or if {@code value} is equal to the JSON
+ * null representation supported by this adaptation (if any)
+ */
+ boolean isNull(Object value);
+
+ /**
+ * Adapts the given value by applying an adaptation function based on the input value.
+ * @param value the subject value to adapt
+ * @return the adapted value which must be either
+ * (1) an intrinsic representation of an implementation-specific JSON scalar value
+ * (e.g. String, Boolean, Number)
+ * OR (2) a {@link JsonArrayAdapter} for an implementation-specific JSON array
+ * OR (3) a {@link JsonObjectAdapter} for an implementation-specific JSON object
+ * OR (4) the input {@code value} if the value is not a recognized
+ * implementation-specific type
+ */
+ Object adapt(Object value);
+
+ /**
+ * Inverts the adaptation function applied to the given value.
+ * @param value the subject adapted value
+ * @return the result of inverting any recognized adaptation in {@code value} OR
+ * {@code value} if no adaptation was recognized. If {@code value} is an instance of
+ * {@link JsonAdapter} the return value is generally the result of
+ * {@link JsonAdapter#unwrap()}. If {@code value} has an intrinsic type (String,
+ * Boolean, Number), the return value may be an implementation-specific representation
+ * of any of these types.
+ */
+ T invert(Object value);
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/spi/JsonAdapter.java b/core/src/main/java/org/everit/json/schema/spi/JsonAdapter.java
new file mode 100644
index 000000000..06f883c59
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/spi/JsonAdapter.java
@@ -0,0 +1,17 @@
+package org.everit.json.schema.spi;
+
+/**
+ * A adapter for a JSON structure type (array or object).
+ *
+ * @param the base type common to the types used in the underlying JSON implementation
+ */
+public interface JsonAdapter {
+
+ /**
+ * Gets the implementation-specific representation of the structure instance.
+ * @return implementation-specific JSON structure representation; generally this is the
+ * delegate of the adapter
+ */
+ T unwrap();
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/spi/JsonArrayAdapter.java b/core/src/main/java/org/everit/json/schema/spi/JsonArrayAdapter.java
new file mode 100644
index 000000000..aca8c157a
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/spi/JsonArrayAdapter.java
@@ -0,0 +1,56 @@
+package org.everit.json.schema.spi;
+
+import java.util.List;
+
+/**
+ * An adapter for a JSON array.
+ *
+ * This interface represents the contract between a provider's JSON array implementation
+ * and a validator utilizing JSON Schema.
+ */
+public interface JsonArrayAdapter extends JsonAdapter {
+
+ /**
+ * Gets the length of the array.
+ * @return the number of elements in the array; i.e. the maximum value for the array
+ * index which can be used {@link #get(int)} to retrieve a value or with
+ * {@link #put(int, Object)} to store a value
+ */
+ int length();
+
+ /**
+ * Retrieves the element of the array at the given index.
+ *
+ * The validator has no need to retrieve values beyond the bounds of the underlying
+ * array. Therefore it is reasonable and expected that an implementation throw an
+ * unchecked exception when {@code index} is outside of the bounds of the array.
+ * However, it is not a requirement that an exception be thrown in this circumstance.
+ *
+ * @param index the index of the element to retrieve
+ * @return the value of the element or {@code null} if no element
+ * exists at the specified index
+ */
+ T get(int index);
+
+ /**
+ * Stores an the element in the array at the given index replacing any existing element
+ * at the same index. Adaptation from intrinsic types or {@link JsonAdapter} subtypes
+ * is performed as needed on the input value.
+ *
+ * The validator has no need to store values beyond the bounds of the underlying
+ * array. Therefore it is reasonable and expected that an implementation throw an
+ * unchecked exception when {@code index} is outside of the bounds of the array.
+ * However, it is not a requirement that an exception be thrown in this circumstance.
+ *
+ * @param index index at which to store the element
+ * @param value the value to store
+ */
+ void put(int index, T value);
+
+ /**
+ * Adapts this array to the {@link List} interface.
+ * @return list adaptation
+ */
+ List toList();
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/spi/JsonObjectAdapter.java b/core/src/main/java/org/everit/json/schema/spi/JsonObjectAdapter.java
new file mode 100644
index 000000000..9ef6ac445
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/spi/JsonObjectAdapter.java
@@ -0,0 +1,61 @@
+package org.everit.json.schema.spi;
+
+import java.util.Map;
+
+/**
+ * An adapter for a JSON object.
+ *
+ * This interface represents the contract between a provider's JSON object implementation
+ * and a validator utilizing JSON Schema.
+ */
+public interface JsonObjectAdapter extends JsonAdapter {
+
+ /**
+ * Gets the length of the object.
+ * @return the number of key-value pairs contained in the object
+ */
+ int length();
+
+ /**
+ * Gets an array containing the keys for the key-value pairs contained in the object.
+ * @return array of keys for values that may be retrieved using {@link #get(String)).
+ */
+ String[] keys();
+
+ /**
+ * Tests whether a given key has a corresponding value in the object.
+ * @param key the subject key
+ * @return {@code true} if this object has a value that corresponds to {@code key}
+ */
+ boolean has(String key);
+
+ /**
+ * Retrieves the value associated with a given key from this object, applying adaptation
+ * to implementation specific types as needed.
+ *
+ * It is expected that the validator will always test for the presence of a key via
+ * the {@link #has(String)} method, before attempting to retrieve it. Therefore it is
+ * reasonable and expected that an implementation throw an unchecked exception when
+ * {@code key} does not exist in object. However, it is not a requirement that an
+ * exception be thrown in this circumstance.
+ *
+ * @param key the subject key
+ * @return the value of the element or {@code null}
+ */
+ T get(String key);
+
+ /**
+ * Replaces any existing value associated with a given key in this object.
+ *
+ * @param key the subject key
+ * @param value the value to store
+ */
+ void put(String key, T value);
+
+ /**
+ * Adapts the object to the {@link Map} interface.
+ * @return map adaptation
+ */
+ Map toMap();
+
+}
diff --git a/core/src/main/java/org/everit/json/schema/spi/package-info.java b/core/src/main/java/org/everit/json/schema/spi/package-info.java
new file mode 100644
index 000000000..0e84ef64f
--- /dev/null
+++ b/core/src/main/java/org/everit/json/schema/spi/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * JSON type service provider interface
+ *
+ * This package specifies an interface to be implemented for a particular JSON type
+ * system to make it compatible with the JSON validation components. An implementation
+ * of this SPI allows validation to performed on JSON objects represented in an
+ * arbitrary type system.
+ */
+package org.everit.json.schema.spi;
\ No newline at end of file
diff --git a/core/src/test/java/org/everit/json/schema/JSONAdaptationTest.java b/core/src/test/java/org/everit/json/schema/JSONAdaptationTest.java
new file mode 100644
index 000000000..0b22668db
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/JSONAdaptationTest.java
@@ -0,0 +1,98 @@
+package org.everit.json.schema;
+
+import org.everit.json.schema.spi.JsonAdapter;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class JSONAdaptationTest {
+
+ private final JSONAdaptation adaptation = new JSONAdaptation();
+
+ @Test
+ public void testArrayType() {
+ assertEquals(JSONArray.class, adaptation.arrayType());
+ }
+
+ @Test
+ public void testObjectType() {
+ assertEquals(JSONObject.class, adaptation.objectType());
+ }
+
+ @Test
+ public void testIsSupportedType() {
+ assertTrue(adaptation.isSupportedType(JSONArray.class));
+ assertTrue(adaptation.isSupportedType(JSONObject.class));
+ assertTrue(adaptation.isSupportedType(JSONObject.NULL.getClass()));
+ }
+
+ @Test
+ public void testSupportedTypes() {
+ final List> types = Arrays.asList(adaptation.supportedTypes());
+ assertEquals(3, types.size());
+ assertTrue(types.contains(JSONArray.class));
+ assertTrue(types.contains(JSONObject.class));
+ assertTrue(types.contains(JSONObject.NULL.getClass()));
+ }
+
+ @Test
+ public void testAdaptIntrinsics() {
+ assertEquals("value", adaptation.adapt("value"));
+ assertEquals(true, adaptation.adapt(true));
+ assertEquals(1, adaptation.adapt(1));
+ assertNull(adaptation.adapt(null));
+ }
+
+ @Test
+ public void testAdaptAdapter() {
+ final JsonAdapter adapter = () -> null;
+ assertSame(adapter, adaptation.adapt(adapter));
+ }
+
+ @Test
+ public void testAdaptJSONNull() {
+ assertNull(adaptation.adapt(JSONObject.NULL));
+ }
+
+ @Test
+ public void testAdaptJSONObject() {
+ final JSONObject object = new JSONObject();
+ final Object result = adaptation.adapt(object);
+ assertTrue(result instanceof JSONObjectAdapter);
+ assertSame(object, ((JSONObjectAdapter) result).unwrap());
+ }
+
+ @Test
+ public void testAdaptJSONArray() {
+ final JSONArray array = new JSONArray();
+ final Object result = adaptation.adapt(array);
+ assertTrue(result instanceof JSONArrayAdapter);
+ assertSame(array, ((JSONArrayAdapter) result).unwrap());
+ }
+
+ @Test
+ public void testInvertIntrinsics() {
+ assertEquals("value", adaptation.invert("value"));
+ assertEquals(true, adaptation.invert(true));
+ assertEquals(1, adaptation.invert(1));
+ assertEquals(JSONObject.NULL, adaptation.invert(null));
+ }
+
+ @Test
+ public void testInvertObjectAdapter() {
+ final JSONObject object = new JSONObject();
+ assertSame(object, adaptation.invert(new JSONObjectAdapter(object)));
+ }
+
+ @Test
+ public void testInvertArrayAdapter() {
+ final JSONArray array = new JSONArray();
+ assertSame(array, adaptation.invert(new JSONArrayAdapter(array)));
+ }
+
+}
diff --git a/core/src/test/java/org/everit/json/schema/JSONArrayAdapterTest.java b/core/src/test/java/org/everit/json/schema/JSONArrayAdapterTest.java
new file mode 100644
index 000000000..eb82907fb
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/JSONArrayAdapterTest.java
@@ -0,0 +1,23 @@
+package org.everit.json.schema;
+
+import org.json.JSONArray;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class JSONArrayAdapterTest {
+
+ @Test
+ public void testAdapter() {
+ final JSONArrayAdapter adapter = new JSONArrayAdapter(
+ new JSONArray().put("value"));
+
+ assertEquals(1, adapter.length());
+ assertEquals("value", adapter.get(0));
+ assertEquals("value", adapter.toList().get(0));
+
+ adapter.put(0, "otherValue");
+ assertEquals("otherValue", adapter.get(0));
+ }
+
+}
diff --git a/core/src/test/java/org/everit/json/schema/JSONObjectAdapterTest.java b/core/src/test/java/org/everit/json/schema/JSONObjectAdapterTest.java
new file mode 100644
index 000000000..3e2f4efdd
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/JSONObjectAdapterTest.java
@@ -0,0 +1,47 @@
+/*
+ * File created on Sep 13, 2018
+ *
+ * Copyright (c) 2018 Carl Harris, Jr
+ * and others as noted
+ *
+ * Licensed 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.everit.json.schema;
+
+import org.json.JSONObject;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JSONObjectAdapterTest {
+
+ @Test
+ public void testAdapter() {
+ final JSONObjectAdapter adapter = new JSONObjectAdapter(
+ new JSONObject().put("key", "value"));
+
+ assertEquals(1, adapter.length());
+ assertTrue(adapter.has("key"));
+ assertEquals("value", adapter.get("key"));
+ assertTrue(Arrays.asList(adapter.keys()).contains("key"));
+ assertTrue(adapter.toMap().containsKey("key"));
+ assertEquals("value", adapter.toMap().get("key"));
+
+ adapter.put("key", "otherValue");
+ assertEquals("otherValue", adapter.get("key"));
+ }
+
+}
diff --git a/core/src/test/java/org/everit/json/schema/JavaxJsonValidatingVisitorTest.java b/core/src/test/java/org/everit/json/schema/JavaxJsonValidatingVisitorTest.java
new file mode 100644
index 000000000..3d21ae0ad
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/JavaxJsonValidatingVisitorTest.java
@@ -0,0 +1,154 @@
+/*
+ * File created on Sep 12, 2018
+ *
+ * Copyright (c) 2018 Carl Harris, Jr
+ * and others as noted
+ *
+ * Licensed 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.everit.json.schema;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.everit.json.schema.javax.json.Jsr353Adaptation;
+import org.everit.json.schema.javax.json.Jsr374Adaptation;
+import org.everit.json.schema.spi.JsonAdaptation;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
+
+@RunWith(JUnitParamsRunner.class)
+public class JavaxJsonValidatingVisitorTest {
+
+ private static final Jsr374Adaptation JSR_374_ADAPTATION = new Jsr374Adaptation();
+ private static final Jsr353Adaptation JSR_353_ADAPTATION = new Jsr353Adaptation();
+
+ private ValidationFailureReporter reporter;
+
+ @Before
+ public void before() {
+ reporter = mock(ValidationFailureReporter.class);
+ }
+
+ public Object[] javaxJsonAdaptations() {
+ return new Object[] { JSR_374_ADAPTATION, JSR_353_ADAPTATION };
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_otherType_noRequires(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, jsonAdaptation);
+ assertFalse(subject.passesTypeCheck(jsonAdaptation.objectType(), false, null));
+ verifyZeroInteractions(reporter);
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_otherType_requires(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, jsonAdaptation);
+ assertFalse(subject.passesTypeCheck(jsonAdaptation.objectType(), true, null));
+ verify(reporter).failure(JsonObject.class, "string");
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_otherType_nullPermitted_nullObject(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor(JsonValue.NULL, reporter, null, jsonAdaptation);
+ assertFalse(subject.passesTypeCheck(jsonAdaptation.objectType(), true, Boolean.TRUE));
+ verifyZeroInteractions(reporter);
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_otherType_nullPermitted_nullReference(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor(null, reporter, null, jsonAdaptation);
+ assertFalse(subject.passesTypeCheck(jsonAdaptation.objectType(), true, Boolean.TRUE));
+ verifyZeroInteractions(reporter);
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_nullPermitted_nonNullValue(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, jsonAdaptation);
+ assertFalse(subject.passesTypeCheck(jsonAdaptation.objectType(), true, Boolean.TRUE));
+ verify(reporter).failure(jsonAdaptation.objectType(), "string");
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_requiresType_nullableIsNull(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor(null, reporter, null, jsonAdaptation);
+ assertFalse(subject.passesTypeCheck(jsonAdaptation.objectType(), true, null));
+ verify(reporter).failure(jsonAdaptation.objectType(), null);
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void passesTypeCheck_sameType(JsonAdaptation> jsonAdaptation) {
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, jsonAdaptation);
+ assertTrue(subject.passesTypeCheck(String.class, true, Boolean.TRUE));
+ verifyZeroInteractions(reporter);
+ }
+
+ public Object[] permittedTypes() {
+ return new Object[] {
+ new Object[] { "str" },
+ new Object[] { 1 },
+ new Object[] { 1L },
+ new Object[] { 1.0 },
+ new Object[] { 1.0f },
+ new Object[] { new BigInteger("42") },
+ new Object[] { new BigDecimal("42.3") },
+ new Object[] { true },
+ new Object[] { null },
+ new Object[] { JsonValue.NULL },
+ new Object[] { JsonValue.FALSE },
+ new Object[] { JsonValue.TRUE },
+ new Object[] { Json.createValue("str") },
+ new Object[] { Json.createValue(1) },
+ new Object[] { Json.createObjectBuilder().build() },
+ new Object[] { Json.createArrayBuilder().build() },
+ };
+ }
+
+ public Object[] notPermittedTypes() {
+ return new Object[] {
+ new Object[] { new ArrayList() },
+ new Object[] { new RuntimeException() }
+ };
+ }
+
+ @Test
+ @Parameters(method = "permittedTypes")
+ public void permittedTypeSuccess(Object subject) {
+ new ValidatingVisitor(subject, reporter, ReadWriteValidator.NONE, JSR_374_ADAPTATION);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ @Parameters(method = "notPermittedTypes")
+ public void notPermittedTypeFailure(Object subject) {
+ new ValidatingVisitor(subject, reporter, ReadWriteValidator.NONE, JSR_374_ADAPTATION);
+ }
+
+}
diff --git a/core/src/test/java/org/everit/json/schema/ObjectComparatorTest.java b/core/src/test/java/org/everit/json/schema/ObjectComparatorTest.java
index 8784152e8..513c3a5b3 100644
--- a/core/src/test/java/org/everit/json/schema/ObjectComparatorTest.java
+++ b/core/src/test/java/org/everit/json/schema/ObjectComparatorTest.java
@@ -2,6 +2,8 @@
import static org.junit.Assert.assertFalse;
+import org.everit.json.schema.spi.JsonArrayAdapter;
+import org.everit.json.schema.spi.JsonObjectAdapter;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
@@ -17,6 +19,12 @@ public class ObjectComparatorTest {
public static final JSONArray EMPTY_ARRAY = new JSONArray();
public static final JSONObject EMPTY_OBJECT = new JSONObject();
+ private static final JsonArrayAdapter EMPTY_ARRAY_ADAPTER =
+ new JSONArrayAdapter(new JSONArray());
+
+ private static final JsonObjectAdapter EMPTY_OBJECT_ADAPTER =
+ new JSONObjectAdapter(new JSONObject());
+
private Object[][] failingCases() {
return new Object[][] {
{ "array, null", EMPTY_ARRAY, null },
@@ -24,7 +32,17 @@ private Object[][] failingCases() {
{ "object, null", EMPTY_OBJECT, null },
{ "arrays with different length", EMPTY_ARRAY, new JSONArray("[null]") },
{ "arrays with different elems", new JSONArray("[true, false]"), new JSONArray("[false, true]") },
- { "objects with different length", EMPTY_OBJECT, new JSONObject("{\"a\":true}") }
+ { "objects with different length", EMPTY_OBJECT, new JSONObject("{\"a\":true}") },
+ { "array adapter, null", EMPTY_ARRAY_ADAPTER, null },
+ { "array adapter, object adapter", EMPTY_ARRAY_ADAPTER, EMPTY_OBJECT_ADAPTER },
+ { "object adapter, null", EMPTY_OBJECT_ADAPTER, null },
+ { "array adapters with different length", EMPTY_ARRAY_ADAPTER, new JSONArrayAdapter(new JSONArray("[null]")) },
+ { "array adapters with different elems", new JSONArrayAdapter(new JSONArray("[true, false]")), new JSONArrayAdapter(new JSONArray("[false, true]")) },
+ { "object adapters with different length", EMPTY_OBJECT_ADAPTER, new JSONObjectAdapter(new JSONObject("{\"a\":true}")) },
+ { "array, array adapter", EMPTY_ARRAY, EMPTY_ARRAY_ADAPTER },
+ { "object, object adapter", EMPTY_OBJECT, EMPTY_OBJECT_ADAPTER },
+ { "array, object adapter", EMPTY_ARRAY, EMPTY_OBJECT_ADAPTER },
+ { "object, array adapter", EMPTY_OBJECT, EMPTY_ARRAY_ADAPTER },
};
}
diff --git a/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java b/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java
index 7d809197c..bd2fd626a 100644
--- a/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java
+++ b/core/src/test/java/org/everit/json/schema/ValidatingVisitorTest.java
@@ -22,6 +22,8 @@
@RunWith(JUnitParamsRunner.class)
public class ValidatingVisitorTest {
+ private static final JSONAdaptation ORG_JSON_ADAPTATION = new JSONAdaptation();
+
private ValidationFailureReporter reporter;
@Before
@@ -31,49 +33,49 @@ public void before() {
@Test
public void passesTypeCheck_otherType_noRequires() {
- ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, ORG_JSON_ADAPTATION);
assertFalse(subject.passesTypeCheck(JSONObject.class, false, null));
verifyZeroInteractions(reporter);
}
@Test
public void passesTypeCheck_otherType_requires() {
- ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, ORG_JSON_ADAPTATION);
assertFalse(subject.passesTypeCheck(JSONObject.class, true, null));
verify(reporter).failure(JSONObject.class, "string");
}
@Test
public void passesTypeCheck_otherType_nullPermitted_nullObject() {
- ValidatingVisitor subject = new ValidatingVisitor(JSONObject.NULL, reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor(JSONObject.NULL, reporter, null, ORG_JSON_ADAPTATION);
assertFalse(subject.passesTypeCheck(JSONObject.class, true, Boolean.TRUE));
verifyZeroInteractions(reporter);
}
@Test
public void passesTypeCheck_otherType_nullPermitted_nullReference() {
- ValidatingVisitor subject = new ValidatingVisitor(null, reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor(null, reporter, null, ORG_JSON_ADAPTATION);
assertFalse(subject.passesTypeCheck(JSONObject.class, true, Boolean.TRUE));
verifyZeroInteractions(reporter);
}
@Test
public void passesTypeCheck_nullPermitted_nonNullValue() {
- ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, ORG_JSON_ADAPTATION);
assertFalse(subject.passesTypeCheck(JSONObject.class, true, Boolean.TRUE));
verify(reporter).failure(JSONObject.class, "string");
}
@Test
public void passesTypeCheck_requiresType_nullableIsNull() {
- ValidatingVisitor subject = new ValidatingVisitor(null, reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor(null, reporter, null, ORG_JSON_ADAPTATION);
assertFalse(subject.passesTypeCheck(JSONObject.class, true, null));
verify(reporter).failure(JSONObject.class, null);
}
@Test
public void passesTypeCheck_sameType() {
- ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null);
+ ValidatingVisitor subject = new ValidatingVisitor("string", reporter, null, ORG_JSON_ADAPTATION);
assertTrue(subject.passesTypeCheck(String.class, true, Boolean.TRUE));
verifyZeroInteractions(reporter);
}
@@ -105,13 +107,13 @@ public Object[] notPermittedTypes() {
@Test
@Parameters(method = "permittedTypes")
public void permittedTypeSuccess(Object subject) {
- new ValidatingVisitor(subject, reporter, ReadWriteValidator.NONE);
+ new ValidatingVisitor(subject, reporter, ReadWriteValidator.NONE, ORG_JSON_ADAPTATION);
}
@Test(expected = IllegalArgumentException.class)
@Parameters(method = "notPermittedTypes")
public void notPermittedTypeFailure(Object subject) {
- new ValidatingVisitor(subject, reporter, ReadWriteValidator.NONE);
+ new ValidatingVisitor(subject, reporter, ReadWriteValidator.NONE, ORG_JSON_ADAPTATION);
}
}
diff --git a/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonArrayAdapterTest.java b/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonArrayAdapterTest.java
new file mode 100644
index 000000000..ebae31c74
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonArrayAdapterTest.java
@@ -0,0 +1,45 @@
+/*
+ * File created on Sep 14, 2018
+ *
+ * Copyright (c) 2018 Carl Harris, Jr
+ * and others as noted
+ *
+ * Licensed 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.everit.json.schema.javax.json;
+
+import org.junit.Test;
+
+import javax.json.Json;
+
+import static org.junit.Assert.assertEquals;
+
+public class JavaxJsonArrayAdapterTest {
+
+ private final JavaxJsonArrayAdapter adapter = new JavaxJsonArrayAdapter(
+ Json.createArrayBuilder().add("value").build());
+
+ @Test
+ public void testAdapter() {
+
+ assertEquals(1, adapter.length());
+ assertEquals(Json.createValue("value"), adapter.get(0));
+ assertEquals(Json.createValue("value"), adapter.toList().get(0));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testAdapterPut() {
+ adapter.put(0, Json.createValue("value"));
+ }
+
+}
diff --git a/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonObjectAdapterTest.java b/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonObjectAdapterTest.java
new file mode 100644
index 000000000..1969891ad
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonObjectAdapterTest.java
@@ -0,0 +1,50 @@
+/*
+ * File created on Sep 14, 2018
+ *
+ * Copyright (c) 2018 Carl Harris, Jr
+ * and others as noted
+ *
+ * Licensed 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.everit.json.schema.javax.json;
+
+import org.junit.Test;
+
+import javax.json.Json;
+
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JavaxJsonObjectAdapterTest {
+
+ private final JavaxJsonObjectAdapter adapter = new JavaxJsonObjectAdapter(
+ Json.createObjectBuilder().add("key", "value").build());
+
+ @Test
+ public void testAdapter() {
+ assertEquals(1, adapter.length());
+ assertTrue(adapter.has("key"));
+ assertEquals(Json.createValue("value"), adapter.get("key"));
+ assertTrue(Arrays.asList(adapter.keys()).contains("key"));
+ assertEquals(Json.createValue("value"), adapter.toMap().get("key"));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testAdapterPut() {
+ adapter.put("key", Json.createValue("value"));
+ }
+
+}
diff --git a/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonValidationTest.java b/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonValidationTest.java
new file mode 100644
index 000000000..4758a28ea
--- /dev/null
+++ b/core/src/test/java/org/everit/json/schema/javax/json/JavaxJsonValidationTest.java
@@ -0,0 +1,170 @@
+package org.everit.json.schema.javax.json;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.everit.json.schema.spi.JsonAdapter;
+import org.everit.json.schema.spi.JsonArrayAdapter;
+import org.everit.json.schema.spi.JsonObjectAdapter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.json.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+@RunWith(JUnitParamsRunner.class)
+public class JavaxJsonValidationTest {
+
+ private static final Jsr374Adaptation JSR_374_ADAPTATION = new Jsr374Adaptation();
+ private static final Jsr353Adaptation JSR_353_ADAPTATION = new Jsr353Adaptation();
+
+ public Object[] javaxJsonAdaptations() {
+ return new Object[] { JSR_374_ADAPTATION, JSR_353_ADAPTATION };
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testArrayType(JavaxJsonAdaptation adaptation) {
+ assertEquals(JsonArray.class, adaptation.arrayType());
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testObjectType(JavaxJsonAdaptation adaptation) {
+ assertEquals(JsonObject.class, adaptation.objectType());
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testIsSupportedType(JavaxJsonAdaptation adaptation) {
+ assertTrue(adaptation.isSupportedType(JsonValue.class));
+ assertTrue(adaptation.isSupportedType(JsonNumber.class));
+ assertTrue(adaptation.isSupportedType(JsonString.class));
+ assertTrue(adaptation.isSupportedType(JsonObject.class));
+ assertTrue(adaptation.isSupportedType(JsonArray.class));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testSupportedTypes(JavaxJsonAdaptation adaptation) {
+ final List> types = Arrays.asList(adaptation.supportedTypes());
+ assertEquals(1, types.size());
+ assertTrue(types.contains(JsonValue.class));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testAdaptIntrinsics(JavaxJsonAdaptation adaptation) {
+ assertEquals("value", adaptation.adapt("value"));
+ assertEquals(true, adaptation.adapt(true));
+ assertEquals(1, adaptation.adapt(1));
+ assertNull(adaptation.adapt(null));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testAdaptAdapter(JavaxJsonAdaptation adaptation) {
+ final JsonAdapter adapter = () -> null;
+ assertSame(adapter, adaptation.adapt(adapter));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testAdaptJsonScalars(JavaxJsonAdaptation adaptation) {
+ assertNull(adaptation.adapt(JsonValue.NULL));
+ assertEquals(true, adaptation.adapt(JsonValue.TRUE));
+ assertEquals(false, adaptation.adapt(JsonValue.FALSE));
+ assertEquals("value", adaptation.adapt(Json.createValue("value")));
+ assertEquals(BigInteger.ONE, adaptation.adapt(Json.createValue(1)));
+ assertEquals(BigInteger.ONE, adaptation.adapt(Json.createValue(1L)));
+ assertEquals(BigInteger.ONE, adaptation.adapt(Json.createValue(BigInteger.ONE)));
+ assertEquals(BigDecimal.valueOf(1.1), adaptation.adapt(Json.createValue(BigDecimal.valueOf(1.1))));
+ assertEquals(BigDecimal.valueOf(1.1), adaptation.adapt(Json.createValue(1.1)));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testAdaptJsonObject(JavaxJsonAdaptation adaptation) {
+ final JsonObject object = Json.createObjectBuilder().build();
+ final Object result = adaptation.adapt(object);
+ assertTrue(result instanceof JsonObjectAdapter);
+ assertSame(object, ((JsonObjectAdapter) result).unwrap());
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testAdaptJsonArray(JavaxJsonAdaptation adaptation) {
+ final JsonArray object = Json.createArrayBuilder().build();
+ final Object result = adaptation.adapt(object);
+ assertTrue(result instanceof JsonArrayAdapter);
+ assertSame(object, ((JsonArrayAdapter) result).unwrap());
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testInvertIntrinsics(JavaxJsonAdaptation adaptation) {
+ assertEquals(JsonValue.NULL, adaptation.invert(null));
+ assertEquals(JsonValue.TRUE, adaptation.invert(true));
+ assertEquals(JsonValue.FALSE, adaptation.invert(false));
+ assertEquals(Json.createValue("value"), adaptation.invert("value"));
+ assertEquals(Json.createValue(1), adaptation.invert(1));
+ assertEquals(Json.createValue(1L), adaptation.invert(1L));
+ assertEquals(Json.createValue(1.0), adaptation.invert(1.0));
+ assertEquals(Json.createValue(BigInteger.ONE), adaptation.invert(BigInteger.ONE));
+ assertEquals(Json.createValue(BigDecimal.ONE), adaptation.invert(BigDecimal.ONE));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testInvertObjectAdapter(JavaxJsonAdaptation adaptation) {
+ final JsonObject object = Json.createObjectBuilder().build();
+ assertSame(object, adaptation.invert(new JavaxJsonObjectAdapter(object)));
+ }
+
+ @Test
+ @Parameters(method = "javaxJsonAdaptations")
+ public void testInvertArrayAdapter(JavaxJsonAdaptation adaptation) {
+ final JsonArray array = Json.createArrayBuilder().build();
+ assertSame(array, adaptation.invert(new JavaxJsonArrayAdapter(array)));
+ }
+
+ @Test
+ public void testNewInstanceDefaultClassLoader() {
+ final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(null);
+ assertTrue(JavaxJsonAdaptation.newInstance() instanceof Jsr374Adaptation);
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+
+ @Test
+ public void testNewInstanceThreadContextClassLoader() {
+ final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ assertTrue(JavaxJsonAdaptation.newInstance() instanceof Jsr374Adaptation);
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+
+ @Test
+ public void testNewInstanceWithClassLoader() {
+ assertTrue(JavaxJsonAdaptation.newInstance(getClass().getClassLoader())
+ instanceof Jsr374Adaptation);
+ }
+
+ @Test
+ public void testDetermineProvider() {
+ assertEquals(JavaxJsonAdaptation.JSR_374_ADAPTATION,
+ JavaxJsonAdaptation.determineProvider("createValue", String.class));
+ assertEquals(JavaxJsonAdaptation.JSR_353_ADAPTATION,
+ JavaxJsonAdaptation.determineProvider(".noSuchMethod"));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInstanceWhenCannotInstantiate() {
+ JavaxJsonAdaptation.newInstance(getClass().getClassLoader(), ".noSuchProviderName");
+ }
+
+}