浏览代码

Merge branch 'expect_type'

* expect_type:
  Update the rest of the tests to confirm the acceptable type.
  Start testing acceptedTypes() for all the added objects. - Fix bug where AllOf was always empty.
  Drop the default impl and update test cases that need it.
  Convert the rest of the testers
  Add type boundary checks for AllOf, AnyOf, OneOf
  Continueing
  Start adding acceptedTypes() overrides with FixedTester and AllItemsTester
  Remove static headers
  Make things cleaner
  Start writing type testers
  Fix AllItemsTester to work for OBJECT
  Add accepted types to Tester interface.
  Move ACCEPT/REJECT out of Tester
Sam Jaffe 6 年之前
父节点
当前提交
937d7b7994
共有 36 个文件被更改,包括 484 次插入79 次删除
  1. 12 0
      src/main/lombok/org/leumasjaffe/json/schema/Schema.java
  2. 6 2
      src/main/lombok/org/leumasjaffe/json/schema/Tester.java
  3. 22 1
      src/main/lombok/org/leumasjaffe/json/schema/factory/SchemaFactory.java
  4. 28 21
      src/main/lombok/org/leumasjaffe/json/schema/factory/SchemaV6Factory.java
  5. 8 1
      src/main/lombok/org/leumasjaffe/json/schema/tester/AllItemsTester.java
  6. 11 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/AllOfTester.java
  7. 11 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/AnyOfTester.java
  8. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/ContainsTester.java
  9. 32 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/EqualsTester.java
  10. 30 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/FixedTester.java
  11. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/FormatTester.java
  12. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/ItemsTester.java
  13. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/NotTester.java
  14. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/NumberTester.java
  15. 11 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/OneOfTester.java
  16. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/PropertyNameTester.java
  17. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/PropertyTester.java
  18. 5 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/SizeTester.java
  19. 29 9
      src/main/lombok/org/leumasjaffe/json/schema/tester/TypeTester.java
  20. 6 0
      src/main/lombok/org/leumasjaffe/json/schema/tester/UniqueItemTester.java
  21. 24 8
      src/test/java/org/leumasjaffe/json/schema/tester/AllItemsTesterTest.java
  22. 17 3
      src/test/java/org/leumasjaffe/json/schema/tester/AllOfTesterTest.java
  23. 17 3
      src/test/java/org/leumasjaffe/json/schema/tester/AnyOfTesterTest.java
  24. 13 5
      src/test/java/org/leumasjaffe/json/schema/tester/ContainsTesterTest.java
  25. 12 0
      src/test/java/org/leumasjaffe/json/schema/tester/FormatTesterTest.java
  26. 16 7
      src/test/java/org/leumasjaffe/json/schema/tester/ItemsTesterTest.java
  27. 31 0
      src/test/java/org/leumasjaffe/json/schema/tester/MockTester.java
  28. 12 2
      src/test/java/org/leumasjaffe/json/schema/tester/NotTesterTest.java
  29. 8 0
      src/test/java/org/leumasjaffe/json/schema/tester/NumberTesterTest.java
  30. 17 3
      src/test/java/org/leumasjaffe/json/schema/tester/OneOfTesterTest.java
  31. 10 4
      src/test/java/org/leumasjaffe/json/schema/tester/PropertyNameTesterTest.java
  32. 17 10
      src/test/java/org/leumasjaffe/json/schema/tester/PropertyTesterTest.java
  33. 12 0
      src/test/java/org/leumasjaffe/json/schema/tester/SizeTesterTest.java
  34. 9 0
      src/test/java/org/leumasjaffe/json/schema/tester/StubTester.java
  35. 8 0
      src/test/java/org/leumasjaffe/json/schema/tester/TypeTesterTest.java
  36. 8 0
      src/test/java/org/leumasjaffe/json/schema/tester/UniqueItemTesterTest.java

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

@@ -1,9 +1,13 @@
 package org.leumasjaffe.json.schema;
 
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
@@ -23,6 +27,14 @@ public class Schema implements Tester {
 		children.putAll(fields);
 	}
 
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		final Set<JsonNodeType> set = new HashSet<>();
+		children.values().stream().map(Tester::acceptedTypes)
+				.forEach(a -> set.addAll(Arrays.asList(a)));
+		return set.toArray(new JsonNodeType[0]);
+	}
+
 	@Override
 	public boolean accepts(JsonNode node) {
 		if (children.isEmpty()) {

+ 6 - 2
src/main/lombok/org/leumasjaffe/json/schema/Tester.java

@@ -1,9 +1,13 @@
 package org.leumasjaffe.json.schema;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 public interface Tester {
-	static final Tester ACCEPT = j -> true;
-	static final Tester REJECT = j -> false;
+	static final JsonNodeType[] ANY = {
+			JsonNodeType.ARRAY, JsonNodeType.OBJECT, JsonNodeType.STRING,
+			JsonNodeType.NUMBER, JsonNodeType.BOOLEAN, JsonNodeType.NULL
+	};
+	JsonNodeType[] acceptedTypes();
 	boolean accepts(final JsonNode node);
 }

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

@@ -1,19 +1,40 @@
 package org.leumasjaffe.json.schema.factory;
 
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.regex.Pattern;
 
 import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Schema;
 import org.leumasjaffe.json.schema.Tester;
+import org.leumasjaffe.json.schema.tester.FixedTester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+import lombok.AllArgsConstructor;
 
 public class SchemaFactory {	
+	@AllArgsConstructor
+	static final class SimpleTester implements Tester {
+		JsonNodeType type;
+		Predicate<JsonNode> acceptor;
+		
+		@Override
+		public JsonNodeType[] acceptedTypes() {
+			return new JsonNodeType[]{ type };
+		}
+		
+		@Override
+		public boolean accepts(JsonNode node) {
+			return acceptor.test(node);
+		}
+	}
+	
 	public final Tester create(final JsonNode object) {
 		switch (object.getNodeType()) {
 		case BOOLEAN:
-			return new Schema(object.asBoolean() ? Tester.ACCEPT : Tester.REJECT);
+			return new Schema(object.asBoolean() ? FixedTester.ACCEPT : FixedTester.REJECT);
 		case OBJECT:
 			final SchemaFactory versioned = getVersionFactory(object.path("$ref").asText());
 			return new Schema(JsonHelper.fields(object, versioned::createMapping));

+ 28 - 21
src/main/lombok/org/leumasjaffe/json/schema/factory/SchemaV6Factory.java

@@ -1,5 +1,8 @@
 package org.leumasjaffe.json.schema.factory;
 
+import static com.fasterxml.jackson.databind.node.JsonNodeType.*;
+
+import java.util.List;
 import java.util.function.Predicate;
 
 import org.leumasjaffe.json.JsonHelper;
@@ -8,6 +11,8 @@ import org.leumasjaffe.json.schema.tester.AllItemsTester;
 import org.leumasjaffe.json.schema.tester.AllOfTester;
 import org.leumasjaffe.json.schema.tester.AnyOfTester;
 import org.leumasjaffe.json.schema.tester.ContainsTester;
+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.NotTester;
@@ -20,7 +25,6 @@ import org.leumasjaffe.json.schema.tester.TypeTester;
 import org.leumasjaffe.json.schema.tester.UniqueItemTester;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 class SchemaV6Factory extends SchemaFactory {
 	@Override
@@ -31,31 +35,34 @@ class SchemaV6Factory extends SchemaFactory {
 	@Override
 	protected Tester createMapping(final String key, final JsonNode value) {
 		switch (key) {
-		case "$id": return Tester.ACCEPT;
-		case "$schema": return Tester.ACCEPT;
+		case "$id": return FixedTester.ACCEPT;
+		case "$schema": return FixedTester.ACCEPT;
 		// case "$ref": ; // TODO Implement reference propagating
-		case "title": return Tester.ACCEPT;
-		case "description": return Tester.ACCEPT;
-		case "default": return Tester.ACCEPT;
-		case "examples": return Tester.ACCEPT;
+		case "title": return FixedTester.ACCEPT;
+		case "description": return FixedTester.ACCEPT;
+		case "default": return FixedTester.ACCEPT;
+		case "examples": return FixedTester.ACCEPT;
 		case "multipleOf": return new NumberTester(d -> d % value.asDouble() == 0);
 		case "maximum": return new NumberTester(d -> d <= value.asDouble());
 		case "exclusiveMaximum": return new NumberTester(d -> d < value.asDouble());
 		case "minimum": return new NumberTester(d -> d >= value.asDouble());
 		case "exclusiveMinimum": return new NumberTester(d -> d > value.asDouble());
-		case "maxLength": return new SizeTester(JsonNodeType.STRING, i -> i < value.asInt());
-		case "minLength": return new SizeTester(JsonNodeType.STRING, i -> i >= value.asInt(0));
-		case "pattern": return j -> j.isTextual() && j.asText().matches(value.asText());
-		case "additionalItems": return new AllItemsTester(create(value));
-		case "items": return value.isArray() ? new ItemsTester(createArray(value)) : new AllItemsTester(create(value));
-		case "maxItems": return new SizeTester(JsonNodeType.ARRAY, i -> i < value.asInt());
-		case "minItems": return new SizeTester(JsonNodeType.ARRAY, i -> i >= value.asInt(0));
-		case "uniqueItems": return value.asBoolean() ? UniqueItemTester.INSTANCE : Tester.ACCEPT;
+		case "maxLength": return new SizeTester(STRING, i -> i < value.asInt());
+		case "minLength": return new SizeTester(STRING, i -> i >= value.asInt(0));
+		case "pattern": return new SimpleTester(STRING, j -> j.asText().matches(value.asText()));
+		case "additionalItems": return new AllItemsTester(ARRAY, create(value));
+		case "items": return value.isArray() ? new ItemsTester(createArray(value)) : new AllItemsTester(ARRAY, create(value));
+		case "maxItems": return new SizeTester(ARRAY, i -> i < value.asInt());
+		case "minItems": return new SizeTester(ARRAY, i -> i >= value.asInt(0));
+		case "uniqueItems": return value.asBoolean() ? UniqueItemTester.INSTANCE : FixedTester.ACCEPT;
 		case "contains": return new ContainsTester(create(value));
-		case "maxProperties": return new SizeTester(JsonNodeType.OBJECT, i -> i < value.asInt());
-		case "minProperties": return new SizeTester(JsonNodeType.OBJECT, i -> i >= value.asInt(0));
-		case "required": return json -> JsonHelper.toArray(value, JsonNode::asText).stream().allMatch(json::has);
-		case "additionalProperties": return j -> JsonHelper.toArray(j).stream().allMatch(create(value)::accepts);
+		case "maxProperties": return new SizeTester(OBJECT, i -> i < value.asInt());
+		case "minProperties": return new SizeTester(OBJECT, i -> i >= value.asInt(0));
+		case "required": {
+			final List<String> reqKeys = JsonHelper.toArray(value, JsonNode::asText);
+			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 "properties": return new PropertyTester(JsonHelper.values(value,
 				(k, v) -> new PropertyTester.Pair(stringEqual(k), create(v))));
@@ -63,8 +70,8 @@ class SchemaV6Factory extends SchemaFactory {
 				(k, v) -> new PropertyTester.Pair(stringMatches(k), create(v))));
 		// case "dependencies": ; // TODO Implement array(required) and object(schema) versions
 		case "propertyNames": return new PropertyNameTester(create(value));
-		case "const": return value::equals;
-		case "enum": return new AnyOfTester(JsonHelper.toArray(value, v -> (Tester) v::equals));
+		case "const": return new EqualsTester(value);
+		case "enum": return new EqualsTester(JsonHelper.toArray(value));
 		case "type": return TypeTester.fromType(value.asText());
 		case "format": return FormatTester.forCode(value.asText());
 		case "allOf": return new AllOfTester(createArray(value));

+ 8 - 1
src/main/lombok/org/leumasjaffe/json/schema/tester/AllItemsTester.java

@@ -6,6 +6,7 @@ import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -14,11 +15,17 @@ import lombok.experimental.FieldDefaults;
 @RequiredArgsConstructor
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class AllItemsTester implements Tester {
+	JsonNodeType type;
 	Tester schema;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{type};
+	}
 
 	@Override
 	public boolean accepts(JsonNode node) {
-		if (!node.isArray()) return false;
+		if (node.getNodeType() != type) return false;
 		List<JsonNode> data = JsonHelper.toArray(node);
 		for (int i = 0; i < data.size(); ++i) {
 			if (!schema.accepts(data.get(i))) {

+ 11 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/AllOfTester.java

@@ -1,11 +1,14 @@
 package org.leumasjaffe.json.schema.tester;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -19,6 +22,14 @@ public class AllOfTester implements Tester {
 	public AllOfTester(Tester...testers) {
 		this(Arrays.asList(testers));
 	}
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		final Set<JsonNodeType> set = new HashSet<>(Arrays.asList(ANY));
+		children.stream().map(Tester::acceptedTypes)
+				.forEach(a -> set.retainAll(Arrays.asList(a)));
+		return set.toArray(new JsonNodeType[0]);
+	}
 
 	public boolean accepts(JsonNode node) {
 		return children.parallelStream().allMatch(t -> t.accepts(node));

+ 11 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/AnyOfTester.java

@@ -1,11 +1,14 @@
 package org.leumasjaffe.json.schema.tester;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -19,6 +22,14 @@ public class AnyOfTester implements Tester {
 	public AnyOfTester(Tester...testers) {
 		this(Arrays.asList(testers));
 	}
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		final Set<JsonNodeType> set = new HashSet<>();
+		children.stream().map(Tester::acceptedTypes)
+				.forEach(a -> set.addAll(Arrays.asList(a)));
+		return set.toArray(new JsonNodeType[0]);
+	}
 
 	public boolean accepts(JsonNode node) {
 		return children.parallelStream().anyMatch(t -> t.accepts(node));

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/ContainsTester.java

@@ -4,6 +4,7 @@ import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -13,6 +14,11 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class ContainsTester implements Tester {
 	Tester schema;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.ARRAY};
+	}
 
 	@Override
 	public boolean accepts(JsonNode node) {

+ 32 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/EqualsTester.java

@@ -0,0 +1,32 @@
+package org.leumasjaffe.json.schema.tester;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.leumasjaffe.json.schema.Tester;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+
+@RequiredArgsConstructor
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class EqualsTester implements Tester {
+	List<JsonNode> values;
+	
+	public EqualsTester(JsonNode...values) {
+		this(Arrays.asList(values));
+	}
+
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return ANY;
+	}
+	
+	public boolean accepts(JsonNode node) {
+		return values.parallelStream().anyMatch(node::equals);
+	}
+}

+ 30 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/FixedTester.java

@@ -0,0 +1,30 @@
+package org.leumasjaffe.json.schema.tester;
+
+import org.leumasjaffe.json.schema.Tester;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+
+@RequiredArgsConstructor
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class FixedTester implements Tester {
+	public static final FixedTester ACCEPT = new FixedTester(true);
+	public static final FixedTester REJECT = new FixedTester(false);
+	
+	boolean returns;
+
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return ANY;
+	}
+
+	@Override
+	public boolean accepts(JsonNode node) {
+		return returns;
+	}
+
+}

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/FormatTester.java

@@ -10,6 +10,7 @@ import org.apache.commons.validator.routines.InetAddressValidator;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -120,6 +121,11 @@ public abstract class FormatTester implements Tester {
 	
 	String format;
 	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[] {JsonNodeType.STRING};
+	}
+	
 	public static Tester forCode(String asText) {
 		switch (asText) {
 		case "date-time": return DATE_TIME;

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/ItemsTester.java

@@ -7,6 +7,7 @@ import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -20,6 +21,11 @@ public class ItemsTester implements Tester {
 	public ItemsTester(Tester...testers) {
 		this(Arrays.asList(testers));
 	}
+
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.ARRAY};
+	}
 	
 	@Override
 	public boolean accepts(JsonNode node) {

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/NotTester.java

@@ -3,6 +3,7 @@ package org.leumasjaffe.json.schema.tester;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -13,6 +14,11 @@ import lombok.experimental.FieldDefaults;
 public class NotTester implements Tester {
 	Tester child;
 
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return child.acceptedTypes();
+	}
+
 	public boolean accepts(JsonNode node) {
 		return !child.accepts(node);
 	}

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/NumberTester.java

@@ -5,6 +5,7 @@ import java.util.function.DoublePredicate;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -14,6 +15,11 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class NumberTester implements Tester {
 	DoublePredicate pred;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.NUMBER};
+	}
 
 	@Override
 	public boolean accepts(JsonNode node) {

+ 11 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/OneOfTester.java

@@ -1,11 +1,14 @@
 package org.leumasjaffe.json.schema.tester;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
@@ -20,6 +23,14 @@ public class OneOfTester implements Tester {
 		this(Arrays.asList(testers));
 	}
 
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		final Set<JsonNodeType> set = new HashSet<>();
+		children.stream().map(Tester::acceptedTypes)
+				.forEach(a -> set.addAll(Arrays.asList(a)));
+		return set.toArray(new JsonNodeType[0]);
+	}
+	
 	public boolean accepts(JsonNode node) {
 		return children.parallelStream().filter(t -> t.accepts(node)).count() == 1;
 	}

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/PropertyNameTester.java

@@ -4,6 +4,7 @@ import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.TextNode;
 
 import lombok.AccessLevel;
@@ -14,6 +15,11 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class PropertyNameTester implements Tester {
 	Tester schema;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.OBJECT};
+	}
 
 	@Override
 	public boolean accepts(JsonNode node) {

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/PropertyTester.java

@@ -10,6 +10,7 @@ import java.util.stream.Collectors;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -30,6 +31,11 @@ public class PropertyTester implements Tester {
 	public PropertyTester(Pair...pairs) {
 		this(Arrays.asList(pairs));
 	}
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.OBJECT};
+	}
 
 	@Override
 	public boolean accepts(final JsonNode node) {

+ 5 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/SizeTester.java

@@ -16,6 +16,11 @@ import lombok.experimental.FieldDefaults;
 public class SizeTester implements Tester {
 	JsonNodeType type;
 	IntPredicate pred;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{type};
+	}
 
 	@Override
 	public boolean accepts(JsonNode node) {

+ 29 - 9
src/main/lombok/org/leumasjaffe/json/schema/tester/TypeTester.java

@@ -1,22 +1,42 @@
 package org.leumasjaffe.json.schema.tester;
 
+import java.util.function.Predicate;
+
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
 
-public abstract class TypeTester implements Tester {
+@RequiredArgsConstructor(access=AccessLevel.PRIVATE)
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class TypeTester implements Tester {
 
-	public static Tester fromType(final String type) {
+	public static TypeTester fromType(final String type) {
 		switch (type) {
-		case "array": return JsonNode::isArray;
-		case "boolean": return JsonNode::isBoolean;
-		case "integer": return JsonNode::isInt;
-		case "null": return JsonNode::isNull;
-		case "number": return JsonNode::isNumber;
-		case "object": return JsonNode::isObject;
-		case "string": return JsonNode::isTextual;
+		case "array": return new TypeTester(JsonNode::isArray);
+		case "boolean": return new TypeTester(JsonNode::isBoolean);
+		case "integer": return new TypeTester(JsonNode::isInt);
+		case "null": return new TypeTester(JsonNode::isNull);
+		case "number": return new TypeTester(JsonNode::isNumber);
+		case "object": return new TypeTester(JsonNode::isObject);
+		case "string": return new TypeTester(JsonNode::isTextual);
 		default: throw new IllegalArgumentException("Invalid type: " + type);
 		}
 	}
+	
+	Predicate<JsonNode> test;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return ANY;
+	}
 
+	@Override
+	public boolean accepts(JsonNode node) {
+		return test.test(node);
+	}
 }

+ 6 - 0
src/main/lombok/org/leumasjaffe/json/schema/tester/UniqueItemTester.java

@@ -6,9 +6,15 @@ import java.util.Set;
 import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 public class UniqueItemTester implements Tester {
 	public static final Tester INSTANCE = new UniqueItemTester();
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.ARRAY};
+	}
 
 	@Override
 	public boolean accepts(final JsonNode node) {

+ 24 - 8
src/test/java/org/leumasjaffe/json/schema/tester/AllItemsTesterTest.java

@@ -1,26 +1,40 @@
 package org.leumasjaffe.json.schema.tester;
 
 import static org.junit.Assert.*;
+import static org.hamcrest.core.Is.is;
+import static com.fasterxml.jackson.databind.node.JsonNodeType.ARRAY;
+import static com.fasterxml.jackson.databind.node.JsonNodeType.OBJECT;
 
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.NullNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 public class AllItemsTesterTest {
 
 	@Test
-	public void testRejectsNonArray() {
-		assertFalse(new AllItemsTester(Tester.ACCEPT).accepts(NullNode.getInstance()));
+	public void testAcceptsType() {
+		assertThat(new AllItemsTester(ARRAY, FixedTester.ACCEPT).acceptedTypes(),
+				is(new JsonNodeType[]{ARRAY}));
+		assertThat(new AllItemsTester(OBJECT, FixedTester.ACCEPT).acceptedTypes(),
+				is(new JsonNodeType[]{OBJECT}));
+	}
+	
+	@Test
+	public void testRejectsNonMatching() {
+		assertFalse(new AllItemsTester(ARRAY, FixedTester.ACCEPT)
+				.accepts(new ObjectNode(JsonNodeFactory.instance)));
+		assertFalse(new AllItemsTester(OBJECT, FixedTester.ACCEPT)
+				.accepts(new ArrayNode(JsonNodeFactory.instance)));
 	}
 
 	@Test
 	public void testAcceptsEmptyArray() {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
-		assertTrue(new AllItemsTester(Tester.ACCEPT).accepts(node));
+		assertTrue(new AllItemsTester(ARRAY, FixedTester.ACCEPT).accepts(node));
 	}
 	
 	@Test
@@ -28,22 +42,24 @@ public class AllItemsTesterTest {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(true);
-		assertFalse(new AllItemsTester(Tester.REJECT).accepts(node));
+		assertFalse(new AllItemsTester(ARRAY, FixedTester.REJECT).accepts(node));
 	}
 	
 	@Test
 	public void testRejectsIfAnyPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(true);
-		assertFalse(new AllItemsTester(JsonNode::asBoolean).accepts(node));
+		assertFalse(new AllItemsTester(ARRAY, test).accepts(node));
 	}
 	
 	@Test
 	public void testAcceptsIfAllPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(true);
 		node.add(true);
-		assertTrue(new AllItemsTester(JsonNode::asBoolean).accepts(node));
+		assertTrue(new AllItemsTester(ARRAY, test).accepts(node));
 	}
 }

+ 17 - 3
src/test/java/org/leumasjaffe/json/schema/tester/AllOfTesterTest.java

@@ -1,6 +1,7 @@
 package org.leumasjaffe.json.schema.tester;
 
 import static org.junit.Assert.*;
+import static org.hamcrest.core.Is.*;
 
 import org.junit.Test;
 import org.leumasjaffe.json.schema.Tester;
@@ -8,12 +9,25 @@ import org.leumasjaffe.json.schema.Tester;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class AllOfTesterTest {
-	Tester isArray = JsonNode::isArray;
-	Tester isObject = JsonNode::isObject;
-	Tester notEmpty = j -> j.size() != 0;
+	StubTester isArray = JsonNode::isArray;
+	StubTester isObject = JsonNode::isObject;
+	StubTester notEmpty = j -> j.size() != 0;
+	
+	@Test
+	public void testAcceptedTypesIsIntersect() {
+		Tester impossible = new AllOfTester(new MockTester(JsonNodeType.OBJECT, null),
+				new MockTester(JsonNodeType.ARRAY, null));
+		assertThat(impossible.acceptedTypes(), is(new JsonNodeType[0]));
+		Tester resticted = new AllOfTester(new MockTester(JsonNodeType.OBJECT, null),
+				isArray);
+		assertThat(resticted.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.OBJECT}));
+		Tester free = new AllOfTester(isObject,	isArray); // lol
+		assertThat(free.acceptedTypes().length, is(Tester.ANY.length));
+	}
 
 	@Test
 	public void testFailsIfAllFail() {

+ 17 - 3
src/test/java/org/leumasjaffe/json/schema/tester/AnyOfTesterTest.java

@@ -1,5 +1,6 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
@@ -8,12 +9,25 @@ import org.leumasjaffe.json.schema.Tester;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class AnyOfTesterTest {
-	Tester isArray = JsonNode::isArray;
-	Tester isObject = JsonNode::isObject;
-	Tester notEmpty = j -> j.size() != 0;
+	StubTester isArray = JsonNode::isArray;
+	StubTester isObject = JsonNode::isObject;
+	StubTester notEmpty = j -> j.size() != 0;
+	
+	@Test
+	public void testAcceptedTypesIsUnion() {
+		Tester impossible = new AnyOfTester(new MockTester(JsonNodeType.OBJECT, null),
+				new MockTester(JsonNodeType.ARRAY, null));
+		assertThat(impossible.acceptedTypes().length, is(2));
+		Tester resticted = new AnyOfTester(new MockTester(JsonNodeType.OBJECT, null),
+				isArray);
+		assertThat(resticted.acceptedTypes().length, is(Tester.ANY.length));
+		Tester free = new AnyOfTester(isObject,	isArray); // lol
+		assertThat(free.acceptedTypes().length, is(Tester.ANY.length));
+	}
 
 	@Test
 	public void testFailsIfAllFail() {

+ 13 - 5
src/test/java/org/leumasjaffe/json/schema/tester/ContainsTesterTest.java

@@ -1,26 +1,33 @@
 package org.leumasjaffe.json.schema.tester;
 
 import static org.junit.Assert.*;
+import static org.hamcrest.core.Is.*;
 
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class ContainsTesterTest {
+	
+	@Test
+	public void testAcceptedTypes() {
+		assertThat(new ContainsTester(FixedTester.ACCEPT).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.ARRAY}));
+	}
 
 	@Test
 	public void testRejectsNonArray() {
-		assertFalse(new ContainsTester(Tester.ACCEPT).accepts(NullNode.getInstance()));
+		assertFalse(new ContainsTester(FixedTester.ACCEPT).accepts(NullNode.getInstance()));
 	}
 
 	@Test
 	public void testRejectsEmptyArray() {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
-		assertFalse(new ContainsTester(Tester.ACCEPT).accepts(node));
+		assertFalse(new ContainsTester(FixedTester.ACCEPT).accepts(node));
 	}
 	
 	@Test
@@ -28,14 +35,15 @@ public class ContainsTesterTest {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(true);
-		assertFalse(new ContainsTester(Tester.REJECT).accepts(node));
+		assertFalse(new ContainsTester(FixedTester.REJECT).accepts(node));
 	}
 	
 	@Test
 	public void testAcceptsIfAnyPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(true);
-		assertTrue(new ContainsTester(JsonNode::asBoolean).accepts(node));
+		assertTrue(new ContainsTester(test).accepts(node));
 	}
 }

+ 12 - 0
src/test/java/org/leumasjaffe/json/schema/tester/FormatTesterTest.java

@@ -1,10 +1,12 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
 import org.leumasjaffe.json.schema.Tester;
 
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 
@@ -18,6 +20,7 @@ public class FormatTesterTest {
 	@Test
 	public void testUUIDMatcher() {
 		Tester test = FormatTester.forCode("uuid");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("00000000-0000-0000-0000-000000000000")));
 		assertFalse(test.accepts(new TextNode("0000000-0000-0000-0000-000000000000")));
@@ -27,6 +30,7 @@ public class FormatTesterTest {
 	@Test
 	public void testDateTimeMatcher() {
 		Tester test = FormatTester.forCode("date-time");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("2000-01-01T21:10:10Z")));
 		assertTrue(test.accepts(new TextNode("2000-01-01T21:10:10+01:00")));
@@ -40,6 +44,7 @@ public class FormatTesterTest {
 	@Test
 	public void testEmailMatcher() {
 		Tester test = FormatTester.forCode("email");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("user@mail.com")));
 		assertFalse(test.accepts(new TextNode("user@mail")));
@@ -49,6 +54,7 @@ public class FormatTesterTest {
 	@Test
 	public void testHostnameMatcher() {
 		Tester test = FormatTester.forCode("hostname");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("google.com")));
 		assertFalse(test.accepts(new TextNode("192.168.0.1")));
@@ -59,6 +65,7 @@ public class FormatTesterTest {
 	@Test
 	public void testipv4Matcher() {
 		Tester test = FormatTester.forCode("ipv4");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("192.168.0.1")));
 		assertFalse(test.accepts(new TextNode("192.168.0.1/24")));
@@ -68,6 +75,7 @@ public class FormatTesterTest {
 	@Test
 	public void testipv6Matcher() {
 		Tester test = FormatTester.forCode("ipv6");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("::1")));
 		assertFalse(test.accepts(new TextNode("[::1]:80")));
@@ -77,6 +85,7 @@ public class FormatTesterTest {
 	@Test
 	public void testURIMatcher() {
 		Tester test = FormatTester.forCode("uri");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertFalse(test.accepts(new TextNode("#/definitions/schemaArray")));
@@ -88,6 +97,7 @@ public class FormatTesterTest {
 	@Test
 	public void testURIReferenceMatcher() {
 		Tester test = FormatTester.forCode("uri-reference");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("#/definitions/schemaArray")));
@@ -99,6 +109,7 @@ public class FormatTesterTest {
 	@Test
 	public void testURITemplateeMatcher() {
 		Tester test = FormatTester.forCode("uri-template");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("#/definitions/schemaArray")));
@@ -110,6 +121,7 @@ public class FormatTesterTest {
 	@Test
 	public void testJsonPointerMatcher() {
 		Tester test = FormatTester.forCode("json-pointer");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("/definitions/schemaArray")));
 		assertFalse(test.accepts(new TextNode("#/definitions/schemaArray")));

+ 16 - 7
src/test/java/org/leumasjaffe/json/schema/tester/ItemsTesterTest.java

@@ -1,26 +1,33 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class ItemsTesterTest {
 
+	@Test
+	public void testAcceptedTypes() {
+		assertThat(new ItemsTester(FixedTester.ACCEPT).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.ARRAY}));
+	}
+	
 	@Test
 	public void testRejectsNonArray() {
-		assertFalse(new ItemsTester(Tester.ACCEPT).accepts(NullNode.getInstance()));
+		assertFalse(new ItemsTester(FixedTester.ACCEPT).accepts(NullNode.getInstance()));
 	}
 
 	@Test
 	public void testAcceptsEmptyArray() {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
-		assertTrue(new ItemsTester(Tester.ACCEPT).accepts(node));
+		assertTrue(new ItemsTester(FixedTester.ACCEPT).accepts(node));
 	}
 	
 	@Test
@@ -28,23 +35,25 @@ public class ItemsTesterTest {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(true);
-		assertFalse(new ItemsTester(Tester.REJECT).accepts(node));
+		assertFalse(new ItemsTester(FixedTester.REJECT).accepts(node));
 	}
 	
 	@Test
 	public void testAcceptsIfNumTestersPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(true);
 		node.add(false);
-		assertTrue(new ItemsTester(JsonNode::asBoolean).accepts(node));
+		assertTrue(new ItemsTester(test).accepts(node));
 	}
 	
 	@Test
 	public void testAcceptsIfNumElemsPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(true);
 		node.add(false);
-		assertTrue(new ItemsTester(JsonNode::asBoolean,
-				Tester.ACCEPT, Tester.REJECT).accepts(node));
+		assertTrue(new ItemsTester(test, FixedTester.ACCEPT, FixedTester.REJECT)
+				.accepts(node));
 	}
 }

+ 31 - 0
src/test/java/org/leumasjaffe/json/schema/tester/MockTester.java

@@ -0,0 +1,31 @@
+package org.leumasjaffe.json.schema.tester;
+
+import org.leumasjaffe.json.schema.Tester;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+public class MockTester implements Tester {
+	JsonNodeType type;
+	Tester actual;
+	
+	MockTester() {
+		this(null, null);
+	}
+		
+	public MockTester(JsonNodeType type, Tester actual) {
+		this.type = type;
+		this.actual = actual;
+	}
+
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[] {type};
+	}
+
+	@Override
+	public boolean accepts(JsonNode node) {
+		return actual.accepts(node);
+	}
+
+}

+ 12 - 2
src/test/java/org/leumasjaffe/json/schema/tester/NotTesterTest.java

@@ -1,5 +1,6 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
@@ -8,20 +9,29 @@ import org.leumasjaffe.json.schema.tester.NotTester;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class NotTesterTest {
+	@Test
+	public void testInheritsAcceptedTypes() {
+		StubTester stub = JsonNode::isNull;
+		assertThat(new NotTester(stub).acceptedTypes(), is(Tester.ANY));
+		MockTester mock = new MockTester(JsonNodeType.STRING, stub);
+		assertThat(new NotTester(mock).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.STRING}));
+	}
 
 	@Test
 	public void testNotInvertsOutputTrue() {
-		Tester impl = JsonNode::isNull;
+		StubTester impl = JsonNode::isNull;
 		assertTrue(impl.accepts(NullNode.getInstance()));
 		assertFalse(new NotTester(impl).accepts(NullNode.getInstance()));
 	}
 
 	@Test
 	public void testNotInvertsOutputFalse() {
-		Tester impl = JsonNode::isNull;
+		StubTester impl = JsonNode::isNull;
 		assertFalse(impl.accepts(BooleanNode.TRUE));
 		assertTrue(new NotTester(impl).accepts(BooleanNode.TRUE));
 	}

+ 8 - 0
src/test/java/org/leumasjaffe/json/schema/tester/NumberTesterTest.java

@@ -1,5 +1,6 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import java.util.function.DoublePredicate;
@@ -7,11 +8,18 @@ import java.util.function.DoublePredicate;
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.node.DoubleNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class NumberTesterTest {
 	DoublePredicate nonZero = d -> d != 0;
 	
+	@Test
+	public void testAcceptedTypeIsArgument() {
+		assertThat(new NumberTester(nonZero).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.NUMBER}));
+	}
+	
 	@Test
 	public void testRejectsNonNumber() {
 		assertFalse(new NumberTester(nonZero).accepts(NullNode.getInstance()));

+ 17 - 3
src/test/java/org/leumasjaffe/json/schema/tester/OneOfTesterTest.java

@@ -1,5 +1,6 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
@@ -8,13 +9,26 @@ import org.leumasjaffe.json.schema.Tester;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 public class OneOfTesterTest {
-	Tester isArray = JsonNode::isArray;
-	Tester isObject = JsonNode::isObject;
-	Tester notEmpty = j -> j.size() != 0;
+	StubTester isArray = JsonNode::isArray;
+	StubTester isObject = JsonNode::isObject;
+	StubTester notEmpty = j -> j.size() != 0;
 
+	@Test
+	public void testAcceptedTypesIsUnion() {
+		Tester impossible = new OneOfTester(new MockTester(JsonNodeType.OBJECT, null),
+				new MockTester(JsonNodeType.ARRAY, null));
+		assertThat(impossible.acceptedTypes().length, is(2));
+		Tester resticted = new OneOfTester(new MockTester(JsonNodeType.OBJECT, null),
+				isArray);
+		assertThat(resticted.acceptedTypes().length, is(Tester.ANY.length));
+		Tester free = new OneOfTester(isObject,	isArray); // lol
+		assertThat(free.acceptedTypes().length, is(Tester.ANY.length));
+	}
+	
 	@Test
 	public void testFailsIfAllFail() {
 		Tester fails = new OneOfTester(isArray, isObject);

+ 10 - 4
src/test/java/org/leumasjaffe/json/schema/tester/PropertyNameTesterTest.java

@@ -1,26 +1,32 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 public class PropertyNameTesterTest {
-
+	@Test
+	public void testAcceptedTypeIsObject() {
+		assertThat(new PropertyNameTester(FixedTester.REJECT).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.OBJECT}));
+	}
+	
 	@Test
 	public void testPassesEmptyObject() {
-		PropertyNameTester test = new PropertyNameTester(Tester.REJECT);
+		PropertyNameTester test = new PropertyNameTester(FixedTester.REJECT);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		assertTrue(test.accepts(node));
 	}
 	
 	@Test
 	public void testRejectsIfNameFails() {
-		PropertyNameTester test = new PropertyNameTester(Tester.REJECT);
+		PropertyNameTester test = new PropertyNameTester(FixedTester.REJECT);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		assertFalse(test.accepts(node));

+ 17 - 10
src/test/java/org/leumasjaffe/json/schema/tester/PropertyTesterTest.java

@@ -1,16 +1,22 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.tester.PropertyTester.Pair;
 
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 public class PropertyTesterTest {
+	@Test
+	public void testAcceptedTypeIsObject() {
+		assertThat(new PropertyTester(new Pair[0]).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.OBJECT}));
+	}
 
 	@Test
 	public void testPassesUnmatchedProperty() {
@@ -22,7 +28,8 @@ public class PropertyTesterTest {
 
 	@Test
 	public void testFailsIfMatchFails() {
-		PropertyTester test = new PropertyTester(new Pair(s -> s.equals("A"), Tester.REJECT));
+		PropertyTester test = new PropertyTester(new Pair(s -> s.equals("A"),
+				FixedTester.REJECT));
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		assertFalse(test.accepts(node));
@@ -31,8 +38,8 @@ public class PropertyTesterTest {
 	@Test
 	public void testFailsIfAnyMatchFails() {
 		PropertyTester test = new PropertyTester(
-				new Pair(s -> s.equals("A"), Tester.ACCEPT),
-				new Pair(s -> s.equals("B"), Tester.REJECT)
+				new Pair(s -> s.equals("A"), FixedTester.ACCEPT),
+				new Pair(s -> s.equals("B"), FixedTester.REJECT)
 				);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
@@ -43,8 +50,8 @@ public class PropertyTesterTest {
 	@Test
 	public void testFailsIfMoreThanOneMatcherForKey() {
 		PropertyTester test = new PropertyTester(
-				new Pair(s -> s.equals("A"), Tester.ACCEPT),
-				new Pair(s -> s.equals("A"), Tester.REJECT)
+				new Pair(s -> s.equals("A"), FixedTester.ACCEPT),
+				new Pair(s -> s.equals("A"), FixedTester.REJECT)
 				);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
@@ -55,8 +62,8 @@ public class PropertyTesterTest {
 	@Test
 	public void testPassesIfAllPass() {
 		PropertyTester test = new PropertyTester(
-				new Pair(s -> s.equals("A"), Tester.ACCEPT),
-				new Pair(s -> s.equals("B"), Tester.ACCEPT)
+				new Pair(s -> s.equals("A"), FixedTester.ACCEPT),
+				new Pair(s -> s.equals("B"), FixedTester.ACCEPT)
 				);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
@@ -67,8 +74,8 @@ public class PropertyTesterTest {
 	@Test
 	public void testPairMatcherCanOperateOnMultipleKeys() {
 		PropertyTester test = new PropertyTester(
-				new Pair(s -> s.length() == 1, Tester.ACCEPT),
-				new Pair(s -> s.equals("B"), Tester.ACCEPT)
+				new Pair(s -> s.length() == 1, FixedTester.ACCEPT),
+				new Pair(s -> s.equals("B"), FixedTester.ACCEPT)
 				);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());

+ 12 - 0
src/test/java/org/leumasjaffe/json/schema/tester/SizeTesterTest.java

@@ -1,5 +1,6 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import java.util.function.IntPredicate;
@@ -14,6 +15,7 @@ import org.leumasjaffe.json.schema.tester.SizeTester;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.TextNode;
@@ -21,6 +23,16 @@ import com.fasterxml.jackson.databind.node.TextNode;
 public class SizeTesterTest {
 	static IntPredicate NON_ZERO = i -> i > 0;
 
+	@Test
+	public void testAcceptedTypeIsArgument() {
+		assertThat(new SizeTester(ARRAY, NON_ZERO).acceptedTypes(),
+				is(new JsonNodeType[]{ARRAY}));
+		assertThat(new SizeTester(OBJECT, NON_ZERO).acceptedTypes(),
+				is(new JsonNodeType[]{OBJECT}));
+		assertThat(new SizeTester(STRING, NON_ZERO).acceptedTypes(),
+				is(new JsonNodeType[]{STRING}));
+	}
+
 	@Test
 	public void arrayMatcherRejectsObject() {
 		final SizeTester notEmptyArray = new SizeTester(ARRAY, NON_ZERO);

+ 9 - 0
src/test/java/org/leumasjaffe/json/schema/tester/StubTester.java

@@ -0,0 +1,9 @@
+package org.leumasjaffe.json.schema.tester;
+
+import org.leumasjaffe.json.schema.Tester;
+
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+interface StubTester extends Tester {
+	default JsonNodeType[] acceptedTypes() { return ANY; }
+}

+ 8 - 0
src/test/java/org/leumasjaffe/json/schema/tester/TypeTesterTest.java

@@ -1,5 +1,6 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
@@ -33,6 +34,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatcheNullNode() {
 		final Tester t = TypeTester.fromType("null");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertTrue(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
@@ -45,6 +47,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatchesBooleanNode() {
 		final Tester t = TypeTester.fromType("boolean");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertTrue(t.accepts(bool));
 		assertFalse(t.accepts(integral));
@@ -57,6 +60,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatchesIntegerNode() {
 		final Tester t = TypeTester.fromType("integer");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertTrue(t.accepts(integral));
@@ -69,6 +73,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatchesDoubleNode() {
 		final Tester t = TypeTester.fromType("number");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertTrue(t.accepts(integral));
@@ -81,6 +86,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatchesTextNode() {
 		final Tester t = TypeTester.fromType("string");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
@@ -93,6 +99,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatchesArrayNode() {
 		final Tester t = TypeTester.fromType("array");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
@@ -105,6 +112,7 @@ public class TypeTesterTest {
 	@Test
 	public void testMatchesObjectNode() {
 		final Tester t = TypeTester.fromType("object");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));

+ 8 - 0
src/test/java/org/leumasjaffe/json/schema/tester/UniqueItemTesterTest.java

@@ -1,14 +1,22 @@
 package org.leumasjaffe.json.schema.tester;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 public class UniqueItemTesterTest {
+	
+	@Test
+	public void testAcceptedTypeIsArray() {
+		assertThat(new UniqueItemTester().acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.ARRAY}));
+	}
 
 	@Test
 	public void testAcceptsEmptyArray() {