Преглед на файлове

Add support for reference/definition

Sam Jaffe преди 6 години
родител
ревизия
d7a9702f97

+ 4 - 2
src/main/lombok/org/leumasjaffe/json/schema/Schema.java

@@ -32,7 +32,7 @@ public class Schema implements Tester {
 	private static final List<String> OBJECT_MATCHERS = Arrays.asList(
 			"maxProperties", "minProperties", "required", "propertyNames",
 			"dependencies");
-
+	
 	Map<String, Tester> children = new HashMap<>();
 	
 	public Schema() {
@@ -46,7 +46,7 @@ public class Schema implements Tester {
 	public Schema(Map<String, Tester> fields) {
 		children.putAll(fields);
 	}
-
+	
 	@Override
 	public JsonNodeType[] acceptedTypes() {
 		final Set<JsonNodeType> set = new HashSet<>();
@@ -61,6 +61,8 @@ public class Schema implements Tester {
 			return true;
 		} else if (children.containsKey(SELF)) {
 			return children.get(SELF).accepts(node);
+		} else if (children.containsKey("$ref")) {
+			return children.get("$ref").accepts(node);
 		} else if (!canProcess(node)) {
 			return false;
 		}

+ 44 - 1
src/main/lombok/org/leumasjaffe/json/schema/factory/SchemaFactory.java

@@ -1,6 +1,8 @@
 package org.leumasjaffe.json.schema.factory;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Predicate;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -13,8 +15,13 @@ import org.leumasjaffe.json.schema.tester.FixedTester;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.JsonNodeType;
 
+import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
 
+@NoArgsConstructor
 public class SchemaFactory {	
 	@AllArgsConstructor
 	static final class SimpleTester implements Tester {
@@ -32,7 +39,43 @@ public class SchemaFactory {
 		}
 	}
 	
+	@RequiredArgsConstructor
+	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+	protected class Definitions {
+		JsonNode localJson;
+		Map<String, Tester> computed = new HashMap<>();
+		
+		public Tester get(final String path) {
+			computed.computeIfAbsent(path, this::createTester);
+			return computed.get(path);
+		}
+		
+		private Tester createTester(final String path) {
+			if (path.startsWith("#")) {
+				JsonNode current = localJson;
+				final String[] tokens = path.substring(2).split("/");
+				for (final String tok : tokens) {
+					if (tok.matches("^\\d+$")) {
+						current = current.path(Integer.parseInt(tok));
+					} else {
+						current = current.path(tok);
+					}
+				}
+				return SchemaFactory.this.create(current);
+			} else {
+				throw new IllegalArgumentException("Can't do URI searches yet...");
+			}
+		}
+	}
+	
+	protected Definitions defs = null;
+	
+	protected SchemaFactory(Definitions defs) {
+		this.defs = defs;
+	}
+
 	public final Tester create(final JsonNode object) {
+		if (defs == null) { defs = this.new Definitions(object); }
 		switch (object.getNodeType()) {
 		case BOOLEAN:
 			return new Schema(object.asBoolean() ? FixedTester.ACCEPT : FixedTester.REJECT);
@@ -53,7 +96,7 @@ public class SchemaFactory {
 			return this;
 		} else {
 			switch (getVersionInt(version)) {
-			case 6: return new SchemaV6Factory();
+			case 6: return new SchemaV6Factory(defs);
 			default:
 				throw new IllegalArgumentException("Unsupported schema version: " + version);
 			}

+ 10 - 3
src/main/lombok/org/leumasjaffe/json/schema/factory/SchemaV6Factory.java

@@ -27,7 +27,14 @@ import org.leumasjaffe.json.schema.tester.UniqueItemTester;
 
 import com.fasterxml.jackson.databind.JsonNode;
 
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
 class SchemaV6Factory extends SchemaFactory {
+	protected SchemaV6Factory(Definitions defs) {
+		super(defs);
+	}
+
 	@Override
 	protected String getVersion() {
 		return "http://json-schema.org/draft-06/schema#";
@@ -38,7 +45,7 @@ class SchemaV6Factory extends SchemaFactory {
 		switch (key) {
 		case "$id": return FixedTester.ACCEPT;
 		case "$schema": return FixedTester.ACCEPT;
-		// case "$ref": ; // TODO Implement reference propagating
+		case "$ref": return defs.get(value.asText());
 		case "title": return FixedTester.ACCEPT;
 		case "description": return FixedTester.ACCEPT;
 		case "default": return FixedTester.ACCEPT;
@@ -64,12 +71,12 @@ class SchemaV6Factory extends SchemaFactory {
 			return new SimpleTester(OBJECT, json -> reqKeys.stream().allMatch(json::has));
 		}
 		case "additionalProperties": return new AllItemsTester(OBJECT, create(value));
-		// case "definitions": ; // TODO Implement definitions creation
+		case "definitions": return FixedTester.ACCEPT;
 		case "properties": return new PropertyTester(JsonHelper.values(value,
 				(k, v) -> new PropertyTester.Pair(stringEqual(k), create(v))));
 		case "patternProperties": return new PropertyTester(JsonHelper.values(value,
 				(k, v) -> new PropertyTester.Pair(stringMatches(k), create(v))));
-	    case "dependencies": // TODO Implement array(required) and object(schema) versions
+	    case "dependencies":
 	    	return new DependencyTester(JsonHelper.fields(value, (k, v) -> {
 	    		if (v.isArray()) return createMapping("required", v);
 	    		else return create(v);

+ 13 - 0
src/test/java/org/leumasjaffe/json/schema/factory/SchemaV6FactoryTest.java

@@ -289,6 +289,19 @@ public class SchemaV6FactoryTest {
 		assertFalse(test.accepts(readTree("{ \"B\": {} }")));
 	}
 	
+	@Test
+	public void testDefinitionsSchemaInLocalJson() {
+		Tester defArray = factory.create(readTree("{" +
+			"\"$ref\": \"#/definitions/0\"," +
+			"\"definitions\": [" +
+				"{ \"type\": \"boolean\" }" +
+			"]" +
+		"}"));
+		assertFalse(defArray.accepts(NullNode.instance));
+		assertFalse(defArray.accepts(new ObjectNode(JsonNodeFactory.instance)));
+		assertTrue(defArray.accepts(BooleanNode.TRUE));
+	}
+	
 	@Test
 	public void testPropertiesSchema() {
 		Tester test = fromSingleElement("{ \"properties\": { \"A\": true, \"B\": false } }");