Browse Source

Start testing items/additionalItems

Sam Jaffe 6 years ago
parent
commit
e074534a84

+ 21 - 0
src/main/lombok/org/leumasjaffe/json/schema/ArrayTester.java

@@ -0,0 +1,21 @@
+package org.leumasjaffe.json.schema;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import lombok.NoArgsConstructor;
+
+public interface ArrayTester extends Tester {
+	@NoArgsConstructor
+	class Status {
+		public boolean accepted = true;
+		public List<JsonNode> unprocessed = new ArrayList<>();
+		
+		public Status(boolean accepted) {
+			this.accepted = accepted;
+		}
+	}
+	Status accepts(List<JsonNode> data);
+}

+ 21 - 0
src/main/lombok/org/leumasjaffe/json/schema/Schema.java

@@ -9,6 +9,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.leumasjaffe.json.JsonHelper;
+
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.JsonNodeType;
 
@@ -25,6 +27,9 @@ public class Schema implements Tester {
 		"exclusiveMaximum");
 	private static final List<String> STRING_MATCHERS = Arrays.asList(
 			"maxLength", "minLength", "pattern", "format");
+	private static final List<String> ARRAY_MATCHERS = Arrays.asList(
+			"maxItems", "minItems", "uniqueItems", "contains");
+
 	Map<String, Tester> children = new HashMap<>();
 	
 	public Schema() {
@@ -59,6 +64,7 @@ public class Schema implements Tester {
 		switch (node.getNodeType()) {
 		case NUMBER: return acceptsNumber(node);
 		case STRING: return acceptsString(node);
+		case ARRAY: return acceptsArray(node);
 		default: return acceptsUniversal(node);
 		}
 	}
@@ -80,6 +86,21 @@ public class Schema implements Tester {
 		}
 		return acceptsUniversal(node);
 	}
+	
+	private boolean acceptsArray(JsonNode node) {
+		for (String key : getKeys(ARRAY_MATCHERS)) {
+			if (!children.get(key).accepts(node)) {
+				return false;
+			}
+		}
+		ArrayTester items = (ArrayTester) children.get("items");
+		ArrayTester addtlItems = (ArrayTester) children.get("additionalItems");
+		ArrayTester.Status status = items.accepts(JsonHelper.toArray(node));
+		if (status.accepted && !status.unprocessed.isEmpty()) {
+			status = addtlItems.accepts(status.unprocessed);
+		}
+		return status.accepted && acceptsUniversal(node);
+	}
 
 	private boolean acceptsUniversal(JsonNode node) {
 		for (String key : getKeys(UNIVERSAL_MATCHERS)) {

+ 11 - 4
src/main/lombok/org/leumasjaffe/json/schema/tester/AllItemsTester.java

@@ -3,6 +3,7 @@ package org.leumasjaffe.json.schema.tester;
 import java.util.List;
 
 import org.leumasjaffe.json.JsonHelper;
+import org.leumasjaffe.json.schema.ArrayTester;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
@@ -14,7 +15,7 @@ import lombok.experimental.FieldDefaults;
 
 @RequiredArgsConstructor
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
-public class AllItemsTester implements Tester {
+public class AllItemsTester implements Tester, ArrayTester {
 	JsonNodeType type;
 	Tester schema;
 	
@@ -26,13 +27,19 @@ public class AllItemsTester implements Tester {
 	@Override
 	public boolean accepts(JsonNode node) {
 		if (node.getNodeType() != type) return false;
-		List<JsonNode> data = JsonHelper.toArray(node);
+		return accepts(JsonHelper.toArray(node)).accepted;
+	}
+
+	@Override
+	public Status accepts(List<JsonNode> data) {
+		Status out = new Status();
 		for (int i = 0; i < data.size(); ++i) {
 			if (!schema.accepts(data.get(i))) {
-				return false;
+				out.accepted = false;
+				break;
 			}
 		}
-		return true;
+		return out;
 	}
 
 }

+ 9 - 1
src/main/lombok/org/leumasjaffe/json/schema/tester/FixedTester.java

@@ -1,5 +1,8 @@
 package org.leumasjaffe.json.schema.tester;
 
+import java.util.List;
+
+import org.leumasjaffe.json.schema.ArrayTester;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
@@ -11,7 +14,7 @@ import lombok.experimental.FieldDefaults;
 
 @RequiredArgsConstructor
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
-public class FixedTester implements Tester {
+public class FixedTester implements Tester, ArrayTester {
 	public static final FixedTester ACCEPT = new FixedTester(true);
 	public static final FixedTester REJECT = new FixedTester(false);
 	
@@ -27,4 +30,9 @@ public class FixedTester implements Tester {
 		return returns;
 	}
 
+	@Override
+	public Status accepts(List<JsonNode> data) {
+		return new Status(returns);
+	}
+
 }

+ 17 - 7
src/main/lombok/org/leumasjaffe/json/schema/tester/ItemsTester.java

@@ -5,6 +5,7 @@ import java.util.List;
 
 import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Tester;
+import org.leumasjaffe.json.schema.ArrayTester;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.JsonNodeType;
@@ -15,7 +16,7 @@ import lombok.experimental.FieldDefaults;
 
 @RequiredArgsConstructor
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
-public class ItemsTester implements Tester {
+public class ItemsTester implements ArrayTester {
 	List<Tester> schemas;
 
 	public ItemsTester(Tester...testers) {
@@ -26,17 +27,26 @@ public class ItemsTester implements Tester {
 	public JsonNodeType[] acceptedTypes() {
 		return new JsonNodeType[]{JsonNodeType.ARRAY};
 	}
-	
+
 	@Override
 	public boolean accepts(JsonNode node) {
 		if (!node.isArray()) return false;
-		List<JsonNode> data = JsonHelper.toArray(node);
-		for (int i = 0; i < Math.min(schemas.size(), data.size()); ++i) {
+		return accepts(JsonHelper.toArray(node)).accepted;
+	}
+
+	@Override
+	public Status accepts(List<JsonNode> data) {
+		Status out = new Status();
+		if (data.size() > schemas.size()) {
+			out.unprocessed.addAll(data.subList(schemas.size(), data.size()));
+			data.removeAll(out.unprocessed);
+		}
+		for (int i = 0; i < data.size(); ++i) {
 			if (!schemas.get(i).accepts(data.get(i))) {
-				return false;
+				out.accepted = false;
+				break;
 			}
 		}
-		return true;
+		return out;
 	}
-
 }

+ 20 - 0
src/test/java/org/leumasjaffe/json/schema/SchemaTest.java

@@ -11,14 +11,17 @@ import org.junit.Test;
 import org.leumasjaffe.json.schema.tester.EqualsTester;
 import org.leumasjaffe.json.schema.tester.FixedTester;
 import org.leumasjaffe.json.schema.tester.FormatTester;
+import org.leumasjaffe.json.schema.tester.ItemsTester;
 import org.leumasjaffe.json.schema.tester.MockTester;
 import org.leumasjaffe.json.schema.tester.NumberTester;
 import org.leumasjaffe.json.schema.tester.SizeTester;
 import org.leumasjaffe.json.schema.tester.StubTester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.BooleanNode;
 import com.fasterxml.jackson.databind.node.DoubleNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 
@@ -131,4 +134,21 @@ public class SchemaTest {
 				.accepts(new TextNode("C")));
 	}
 
+	@Test
+	public void testHandlesAdditionalItemsMatcherWhenArrayItems() {
+		Map<String, Tester> tests = new HashMap<>();
+		tests.put("items", new ItemsTester(getNumberSchema(), getStringSchema()));
+		tests.put("additionalItems", FixedTester.REJECT);
+		Schema schema = new Schema(tests);
+
+		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
+		node.add(0.5);
+		node.add("https://google.com");
+		assertTrue(schema.accepts(node));
+		node.add(true);
+		assertFalse(schema.accepts(node));
+
+		tests.put("additionalItems", FixedTester.ACCEPT);
+		assertTrue(new Schema(tests).accepts(node));
+	}
 }