Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A proposal for supporting the JSON-P types of the javax.json package. #220

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@
<artifactId>json</artifactId>
<version>20180130</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.2</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.everit.json.schema;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import org.everit.json.schema.spi.JsonAdaptation;
import org.everit.json.schema.spi.JsonArrayAdapter;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -10,28 +10,32 @@
import java.util.function.IntFunction;
import java.util.stream.IntStream;

import org.json.JSONArray;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

class ArraySchemaValidatingVisitor extends Visitor {

private final Object subject;

private final ValidatingVisitor owner;

private JSONArray arraySubject;
private final JsonAdaptation<?> jsonAdaptation;

private JsonArrayAdapter<?> arraySubject;

private ArraySchema arraySchema;

private int subjectLength;

public ArraySchemaValidatingVisitor(Object subject, ValidatingVisitor owner) {
public ArraySchemaValidatingVisitor(Object subject, ValidatingVisitor owner, JsonAdaptation<?> jsonAdaptation) {
this.subject = subject;
this.owner = requireNonNull(owner, "owner cannot be null");
this.jsonAdaptation = jsonAdaptation;
}

@Override void visitArraySchema(ArraySchema arraySchema) {
if (owner.passesTypeCheck(JSONArray.class, arraySchema.requiresArray(), arraySchema.isNullable())) {
this.arraySubject = (JSONArray) subject;
if (owner.passesTypeCheck(jsonAdaptation.arrayType(), arraySchema.requiresArray(), arraySchema.isNullable())) {
this.arraySubject = (JsonArrayAdapter) jsonAdaptation.adapt(subject);
this.subjectLength = arraySubject.length();
this.arraySchema = arraySchema;
super.visitArraySchema(arraySchema);
Expand Down
12 changes: 9 additions & 3 deletions core/src/main/java/org/everit/json/schema/EnumSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.util.Set;
import java.util.stream.Collectors;
import org.everit.json.schema.internal.JSONPrinter;
import org.everit.json.schema.spi.JsonArrayAdapter;
import org.everit.json.schema.spi.JsonObjectAdapter;
import org.json.JSONArray;
import org.json.JSONObject;

Expand All @@ -18,11 +20,15 @@
public class EnumSchema extends Schema {

static Object toJavaValue(Object orig) {
if (orig instanceof JSONArray) {
if (orig instanceof JsonArrayAdapter) {
return ((JsonArrayAdapter) orig).toList();
} else if (orig instanceof JsonObjectAdapter) {
return ((JsonObjectAdapter) orig).toMap();
} else if (orig instanceof JSONArray) { // recognize this type to support test cases that don't adapt it
return ((JSONArray) orig).toList();
} else if (orig instanceof JSONObject) {
} else if (orig instanceof JSONObject) { // recognize this type to support test cases that don't adapt it
return ((JSONObject) orig).toMap();
} else if (orig == JSONObject.NULL) {
} else if (orig == JSONObject.NULL) { // recognize this value to support test cases that don't adapt it
return null;
} else {
return orig;
Expand Down
72 changes: 72 additions & 0 deletions core/src/main/java/org/everit/json/schema/JSONAdaptation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.everit.json.schema;

import org.everit.json.schema.spi.JsonAdaptation;
import org.everit.json.schema.spi.JsonAdapter;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.stream.Stream;

/**
* A {@link JsonAdaptation} that uses {@link JSONArray} as the array type and
* {@link JSONObject} as the object type.
*
*/
class JSONAdaptation implements JsonAdaptation<Object> {

private static final Class<?>[] SUPPORTED_TYPES = {
JSONArray.class,
JSONObject.class,
JSONObject.NULL.getClass()
};

@Override
public Class<?> arrayType() {
return JSONArray.class;
}

@Override
public Class<?> objectType() {
return JSONObject.class;
}

@Override
public Class<?>[] supportedTypes() {
return SUPPORTED_TYPES;
}

@Override
public boolean isSupportedType(Class<?> type) {
return Stream.of(SUPPORTED_TYPES).anyMatch(t -> t.isAssignableFrom(type));
}

@Override
public boolean isNull(Object value) {
return value == null || JSONObject.NULL.equals(value);
}

@Override
public Object adapt(Object value) {
if (JSONObject.NULL.equals(value)) {
return null;
} else if (value instanceof JSONArray) {
return new JSONArrayAdapter((JSONArray) value);
} else if (value instanceof JSONObject) {
return new JSONObjectAdapter((JSONObject) value);
} else {
return value;
}
}

@Override
public Object invert(Object value) {
if (value == null) {
return JSONObject.NULL;
} else if (value instanceof JsonAdapter) {
return ((JsonAdapter) value).unwrap();
} else {
return value;
}
}

}
42 changes: 42 additions & 0 deletions core/src/main/java/org/everit/json/schema/JSONArrayAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.everit.json.schema;

import org.everit.json.schema.spi.JsonArrayAdapter;
import org.json.JSONArray;

import java.util.List;

/**
* A {@link JsonArrayAdapter} that delegates to a {@link JSONArray}.
*/
class JSONArrayAdapter implements JsonArrayAdapter<Object> {

private final JSONArray delegate;

JSONArrayAdapter(JSONArray delegate) {
this.delegate = delegate;
}

@Override
public Object unwrap() {
return delegate;
}

public Object get(int index) {
return delegate.get(index);
}

public int length() {
return delegate.length();
}

@Override
public void put(int index, Object value) {
delegate.put(index, value);
}

@Override
public List<Object> toList() {
return delegate.toList();
}

}
54 changes: 54 additions & 0 deletions core/src/main/java/org/everit/json/schema/JSONObjectAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.everit.json.schema;

import org.everit.json.schema.spi.JsonObjectAdapter;
import org.json.JSONObject;

import java.util.Map;

/**
* A {@link JsonObjectAdapter} that delegates to a {@link JSONObject}.
*/
class JSONObjectAdapter implements JsonObjectAdapter<Object> {

private final JSONObject delegate;

JSONObjectAdapter(JSONObject delegate) {
this.delegate = delegate;
}

@Override
public Object unwrap() {
return delegate;
}

@Override
public int length() {
return delegate.length();
}

@Override
public String[] keys() {
return JSONObject.getNames(delegate);
}

@Override
public boolean has(String key) {
return delegate.has(key);
}

@Override
public Object get(String key) {
return delegate.get(key);
}

@Override
public void put(String key, Object value) {
delegate.put(key, value);
}

@Override
public Map<String, Object> toMap() {
return delegate.toMap();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.everit.json.schema;

import org.everit.json.schema.spi.JsonAdaptation;

import static java.lang.String.format;

import java.math.BigDecimal;
Expand All @@ -18,23 +20,26 @@ class NumberSchemaValidatingVisitor extends Visitor {

private final ValidatingVisitor owner;

private final JsonAdaptation<?> jsonAdaptation;

private boolean exclusiveMinimum;

private boolean exclusiveMaximum;

private double numberSubject;

NumberSchemaValidatingVisitor(Object subject, ValidatingVisitor owner) {
NumberSchemaValidatingVisitor(Object subject, ValidatingVisitor owner, JsonAdaptation<?> jsonAdaptation) {
this.subject = subject;
this.owner= owner;
this.jsonAdaptation = jsonAdaptation;
}

@Override void visitNumberSchema(NumberSchema numberSchema) {
if (owner.passesTypeCheck(Number.class, numberSchema.isRequiresNumber(), numberSchema.isNullable())) {
if (!INTEGRAL_TYPES.contains(subject.getClass()) && numberSchema.requiresInteger()) {
owner.failure(Integer.class, subject);
} else {
this.numberSubject = ((Number) subject).doubleValue();
this.numberSubject = ((Number) jsonAdaptation.adapt(subject)).doubleValue();
super.visitNumberSchema(numberSchema);
}
}
Expand Down
42 changes: 29 additions & 13 deletions core/src/main/java/org/everit/json/schema/ObjectComparator.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package org.everit.json.schema;

import java.util.Arrays;
import java.util.Objects;
import java.util.*;

import org.everit.json.schema.spi.JsonArrayAdapter;
import org.everit.json.schema.spi.JsonObjectAdapter;
import org.json.JSONArray;
import org.json.JSONObject;

/**
* Deep-equals implementation on primitive wrappers, {@link JSONObject} and {@link JSONArray}.
* Deep-equals implementation on primitive wrappers, array and object adapters, and
* {@link JSONObject} and {@link JSONArray}. {@link JSONArray} and {@link JSONObject} are
* included in order to support test cases without the need to first wrap in an adapter.
*
*/
public final class ObjectComparator {

Expand All @@ -21,21 +25,32 @@ public final class ObjectComparator {
* @return {@code true} if the two objects are equal, {@code false} otherwise
*/
public static boolean deepEquals(Object obj1, Object obj2) {
if (obj1 instanceof JSONArray) {
if (!(obj2 instanceof JSONArray)) {
if (obj1 instanceof JsonArrayAdapter) {
if (!(obj2 instanceof JsonArrayAdapter)) {
return false;
}
return deepEqualArrays((JSONArray) obj1, (JSONArray) obj2);
} else if (obj1 instanceof JSONObject) {
if (!(obj2 instanceof JSONObject)) {
return deepEqualArrays((JsonArrayAdapter) obj1, (JsonArrayAdapter) obj2);
} else if (obj1 instanceof JsonObjectAdapter) {
if (!(obj2 instanceof JsonObjectAdapter)) {
return false;
}
return deepEqualObjects((JSONObject) obj1, (JSONObject) obj2);
return deepEqualObjects((JsonObjectAdapter) obj1, (JsonObjectAdapter) obj2);
} else if (obj1 instanceof JSONArray) { // continue to recognize the JSONArray type to support
if (!(obj2 instanceof JSONArray)) { // test cases that don't adapt org.json types
return false;
}
return deepEqualArrays(new JSONArrayAdapter((JSONArray) obj1), new JSONArrayAdapter((JSONArray) obj2));
} else if (obj1 instanceof JSONObject) { // continue to recognize the JSONArray type to support
if (!(obj2 instanceof JSONObject)) { // test cases that don't adapt org.json types
return false;
}
return deepEqualObjects(new JSONObjectAdapter((JSONObject) obj1), new JSONObjectAdapter((JSONObject) obj2));
}

return Objects.equals(obj1, obj2);
}

private static boolean deepEqualArrays(JSONArray arr1, JSONArray arr2) {
private static boolean deepEqualArrays(JsonArrayAdapter arr1, JsonArrayAdapter arr2) {
if (arr1.length() != arr2.length()) {
return false;
}
Expand All @@ -47,16 +62,17 @@ private static boolean deepEqualArrays(JSONArray arr1, JSONArray arr2) {
return true;
}

private static String[] sortedNamesOf(JSONObject obj) {
String[] raw = JSONObject.getNames(obj);
private static String[] sortedNamesOf(JsonObjectAdapter obj) {
String[] raw = obj.keys();
if (raw == null) {
return null;
}
Arrays.sort(raw, String.CASE_INSENSITIVE_ORDER);
return raw;
}

private static boolean deepEqualObjects(JSONObject jsonObj1, JSONObject jsonObj2) {
private static boolean deepEqualObjects(JsonObjectAdapter jsonObj1,
JsonObjectAdapter jsonObj2) {
String[] obj1Names = sortedNamesOf(jsonObj1);
if (!Arrays.equals(obj1Names, sortedNamesOf(jsonObj2))) {
return false;
Expand Down