浏览代码

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;
 package org.leumasjaffe.json.schema;
 
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.NoArgsConstructor;
@@ -23,6 +27,14 @@ public class Schema implements Tester {
 		children.putAll(fields);
 		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
 	@Override
 	public boolean accepts(JsonNode node) {
 	public boolean accepts(JsonNode node) {
 		if (children.isEmpty()) {
 		if (children.isEmpty()) {

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

@@ -1,9 +1,13 @@
 package org.leumasjaffe.json.schema;
 package org.leumasjaffe.json.schema;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 public interface Tester {
 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);
 	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;
 package org.leumasjaffe.json.schema.factory;
 
 
 import java.util.List;
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.regex.Pattern;
 import java.util.regex.Pattern;
 
 
 import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.JsonHelper;
 import org.leumasjaffe.json.schema.Schema;
 import org.leumasjaffe.json.schema.Schema;
 import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.Tester;
+import org.leumasjaffe.json.schema.tester.FixedTester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+import lombok.AllArgsConstructor;
 
 
 public class SchemaFactory {	
 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) {
 	public final Tester create(final JsonNode object) {
 		switch (object.getNodeType()) {
 		switch (object.getNodeType()) {
 		case BOOLEAN:
 		case BOOLEAN:
-			return new Schema(object.asBoolean() ? Tester.ACCEPT : Tester.REJECT);
+			return new Schema(object.asBoolean() ? FixedTester.ACCEPT : FixedTester.REJECT);
 		case OBJECT:
 		case OBJECT:
 			final SchemaFactory versioned = getVersionFactory(object.path("$ref").asText());
 			final SchemaFactory versioned = getVersionFactory(object.path("$ref").asText());
 			return new Schema(JsonHelper.fields(object, versioned::createMapping));
 			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;
 package org.leumasjaffe.json.schema.factory;
 
 
+import static com.fasterxml.jackson.databind.node.JsonNodeType.*;
+
+import java.util.List;
 import java.util.function.Predicate;
 import java.util.function.Predicate;
 
 
 import org.leumasjaffe.json.JsonHelper;
 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.AllOfTester;
 import org.leumasjaffe.json.schema.tester.AnyOfTester;
 import org.leumasjaffe.json.schema.tester.AnyOfTester;
 import org.leumasjaffe.json.schema.tester.ContainsTester;
 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.FormatTester;
 import org.leumasjaffe.json.schema.tester.ItemsTester;
 import org.leumasjaffe.json.schema.tester.ItemsTester;
 import org.leumasjaffe.json.schema.tester.NotTester;
 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 org.leumasjaffe.json.schema.tester.UniqueItemTester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 class SchemaV6Factory extends SchemaFactory {
 class SchemaV6Factory extends SchemaFactory {
 	@Override
 	@Override
@@ -31,31 +35,34 @@ class SchemaV6Factory extends SchemaFactory {
 	@Override
 	@Override
 	protected Tester createMapping(final String key, final JsonNode value) {
 	protected Tester createMapping(final String key, final JsonNode value) {
 		switch (key) {
 		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 "$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 "multipleOf": return new NumberTester(d -> d % value.asDouble() == 0);
 		case "maximum": return new NumberTester(d -> d <= value.asDouble());
 		case "maximum": return new NumberTester(d -> d <= value.asDouble());
 		case "exclusiveMaximum": 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 "minimum": return new NumberTester(d -> d >= value.asDouble());
 		case "exclusiveMinimum": 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 "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 "definitions": ; // TODO Implement definitions creation
 		case "properties": return new PropertyTester(JsonHelper.values(value,
 		case "properties": return new PropertyTester(JsonHelper.values(value,
 				(k, v) -> new PropertyTester.Pair(stringEqual(k), create(v))));
 				(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))));
 				(k, v) -> new PropertyTester.Pair(stringMatches(k), create(v))));
 		// case "dependencies": ; // TODO Implement array(required) and object(schema) versions
 		// case "dependencies": ; // TODO Implement array(required) and object(schema) versions
 		case "propertyNames": return new PropertyNameTester(create(value));
 		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 "type": return TypeTester.fromType(value.asText());
 		case "format": return FormatTester.forCode(value.asText());
 		case "format": return FormatTester.forCode(value.asText());
 		case "allOf": return new AllOfTester(createArray(value));
 		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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -14,11 +15,17 @@ import lombok.experimental.FieldDefaults;
 @RequiredArgsConstructor
 @RequiredArgsConstructor
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class AllItemsTester implements Tester {
 public class AllItemsTester implements Tester {
+	JsonNodeType type;
 	Tester schema;
 	Tester schema;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{type};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(JsonNode node) {
 	public boolean accepts(JsonNode node) {
-		if (!node.isArray()) return false;
+		if (node.getNodeType() != type) return false;
 		List<JsonNode> data = JsonHelper.toArray(node);
 		List<JsonNode> data = JsonHelper.toArray(node);
 		for (int i = 0; i < data.size(); ++i) {
 		for (int i = 0; i < data.size(); ++i) {
 			if (!schema.accepts(data.get(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;
 package org.leumasjaffe.json.schema.tester;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -19,6 +22,14 @@ public class AllOfTester implements Tester {
 	public AllOfTester(Tester...testers) {
 	public AllOfTester(Tester...testers) {
 		this(Arrays.asList(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) {
 	public boolean accepts(JsonNode node) {
 		return children.parallelStream().allMatch(t -> t.accepts(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;
 package org.leumasjaffe.json.schema.tester;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -19,6 +22,14 @@ public class AnyOfTester implements Tester {
 	public AnyOfTester(Tester...testers) {
 	public AnyOfTester(Tester...testers) {
 		this(Arrays.asList(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) {
 	public boolean accepts(JsonNode node) {
 		return children.parallelStream().anyMatch(t -> t.accepts(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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -13,6 +14,11 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class ContainsTester implements Tester {
 public class ContainsTester implements Tester {
 	Tester schema;
 	Tester schema;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.ARRAY};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(JsonNode node) {
 	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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -120,6 +121,11 @@ public abstract class FormatTester implements Tester {
 	
 	
 	String format;
 	String format;
 	
 	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[] {JsonNodeType.STRING};
+	}
+	
 	public static Tester forCode(String asText) {
 	public static Tester forCode(String asText) {
 		switch (asText) {
 		switch (asText) {
 		case "date-time": return DATE_TIME;
 		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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -20,6 +21,11 @@ public class ItemsTester implements Tester {
 	public ItemsTester(Tester...testers) {
 	public ItemsTester(Tester...testers) {
 		this(Arrays.asList(testers));
 		this(Arrays.asList(testers));
 	}
 	}
+
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.ARRAY};
+	}
 	
 	
 	@Override
 	@Override
 	public boolean accepts(JsonNode node) {
 	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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -13,6 +14,11 @@ import lombok.experimental.FieldDefaults;
 public class NotTester implements Tester {
 public class NotTester implements Tester {
 	Tester child;
 	Tester child;
 
 
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return child.acceptedTypes();
+	}
+
 	public boolean accepts(JsonNode node) {
 	public boolean accepts(JsonNode node) {
 		return !child.accepts(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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -14,6 +15,11 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class NumberTester implements Tester {
 public class NumberTester implements Tester {
 	DoublePredicate pred;
 	DoublePredicate pred;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.NUMBER};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(JsonNode node) {
 	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;
 package org.leumasjaffe.json.schema.tester;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -20,6 +23,14 @@ public class OneOfTester implements Tester {
 		this(Arrays.asList(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) {
 	public boolean accepts(JsonNode node) {
 		return children.parallelStream().filter(t -> t.accepts(node)).count() == 1;
 		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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.TextNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
@@ -14,6 +15,11 @@ import lombok.experimental.FieldDefaults;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class PropertyNameTester implements Tester {
 public class PropertyNameTester implements Tester {
 	Tester schema;
 	Tester schema;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.OBJECT};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(JsonNode node) {
 	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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
@@ -30,6 +31,11 @@ public class PropertyTester implements Tester {
 	public PropertyTester(Pair...pairs) {
 	public PropertyTester(Pair...pairs) {
 		this(Arrays.asList(pairs));
 		this(Arrays.asList(pairs));
 	}
 	}
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.OBJECT};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(final JsonNode node) {
 	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 {
 public class SizeTester implements Tester {
 	JsonNodeType type;
 	JsonNodeType type;
 	IntPredicate pred;
 	IntPredicate pred;
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{type};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(JsonNode node) {
 	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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import java.util.function.Predicate;
+
 import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 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) {
 		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);
 		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 org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 
 
 public class UniqueItemTester implements Tester {
 public class UniqueItemTester implements Tester {
 	public static final Tester INSTANCE = new UniqueItemTester();
 	public static final Tester INSTANCE = new UniqueItemTester();
+	
+	@Override
+	public JsonNodeType[] acceptedTypes() {
+		return new JsonNodeType[]{JsonNodeType.ARRAY};
+	}
 
 
 	@Override
 	@Override
 	public boolean accepts(final JsonNode node) {
 	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;
 package org.leumasjaffe.json.schema.tester;
 
 
 import static org.junit.Assert.*;
 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.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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 {
 public class AllItemsTesterTest {
 
 
 	@Test
 	@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
 	@Test
 	public void testAcceptsEmptyArray() {
 	public void testAcceptsEmptyArray() {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
-		assertTrue(new AllItemsTester(Tester.ACCEPT).accepts(node));
+		assertTrue(new AllItemsTester(ARRAY, FixedTester.ACCEPT).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
@@ -28,22 +42,24 @@ public class AllItemsTesterTest {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(false);
 		node.add(true);
 		node.add(true);
-		assertFalse(new AllItemsTester(Tester.REJECT).accepts(node));
+		assertFalse(new AllItemsTester(ARRAY, FixedTester.REJECT).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
 	public void testRejectsIfAnyPass() {
 	public void testRejectsIfAnyPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(false);
 		node.add(true);
 		node.add(true);
-		assertFalse(new AllItemsTester(JsonNode::asBoolean).accepts(node));
+		assertFalse(new AllItemsTester(ARRAY, test).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
 	public void testAcceptsIfAllPass() {
 	public void testAcceptsIfAllPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(true);
 		node.add(true);
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
+import static org.hamcrest.core.Is.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
 import org.leumasjaffe.json.schema.Tester;
 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.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 
 
 public class AllOfTesterTest {
 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
 	@Test
 	public void testFailsIfAllFail() {
 	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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 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.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 
 
 public class AnyOfTesterTest {
 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
 	@Test
 	public void testFailsIfAllFail() {
 	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;
 package org.leumasjaffe.json.schema.tester;
 
 
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
+import static org.hamcrest.core.Is.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 
 
 public class ContainsTesterTest {
 public class ContainsTesterTest {
+	
+	@Test
+	public void testAcceptedTypes() {
+		assertThat(new ContainsTester(FixedTester.ACCEPT).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.ARRAY}));
+	}
 
 
 	@Test
 	@Test
 	public void testRejectsNonArray() {
 	public void testRejectsNonArray() {
-		assertFalse(new ContainsTester(Tester.ACCEPT).accepts(NullNode.getInstance()));
+		assertFalse(new ContainsTester(FixedTester.ACCEPT).accepts(NullNode.getInstance()));
 	}
 	}
 
 
 	@Test
 	@Test
 	public void testRejectsEmptyArray() {
 	public void testRejectsEmptyArray() {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
-		assertFalse(new ContainsTester(Tester.ACCEPT).accepts(node));
+		assertFalse(new ContainsTester(FixedTester.ACCEPT).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
@@ -28,14 +35,15 @@ public class ContainsTesterTest {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(false);
 		node.add(true);
 		node.add(true);
-		assertFalse(new ContainsTester(Tester.REJECT).accepts(node));
+		assertFalse(new ContainsTester(FixedTester.REJECT).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
 	public void testAcceptsIfAnyPass() {
 	public void testAcceptsIfAnyPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(false);
 		node.add(true);
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
 import org.leumasjaffe.json.schema.Tester;
 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.NullNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 
 
@@ -18,6 +20,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testUUIDMatcher() {
 	public void testUUIDMatcher() {
 		Tester test = FormatTester.forCode("uuid");
 		Tester test = FormatTester.forCode("uuid");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("00000000-0000-0000-0000-000000000000")));
 		assertTrue(test.accepts(new TextNode("00000000-0000-0000-0000-000000000000")));
 		assertFalse(test.accepts(new TextNode("0000000-0000-0000-0000-000000000000")));
 		assertFalse(test.accepts(new TextNode("0000000-0000-0000-0000-000000000000")));
@@ -27,6 +30,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testDateTimeMatcher() {
 	public void testDateTimeMatcher() {
 		Tester test = FormatTester.forCode("date-time");
 		Tester test = FormatTester.forCode("date-time");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("2000-01-01T21:10:10Z")));
 		assertTrue(test.accepts(new TextNode("2000-01-01T21:10:10Z")));
 		assertTrue(test.accepts(new TextNode("2000-01-01T21:10:10+01:00")));
 		assertTrue(test.accepts(new TextNode("2000-01-01T21:10:10+01:00")));
@@ -40,6 +44,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testEmailMatcher() {
 	public void testEmailMatcher() {
 		Tester test = FormatTester.forCode("email");
 		Tester test = FormatTester.forCode("email");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("user@mail.com")));
 		assertTrue(test.accepts(new TextNode("user@mail.com")));
 		assertFalse(test.accepts(new TextNode("user@mail")));
 		assertFalse(test.accepts(new TextNode("user@mail")));
@@ -49,6 +54,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testHostnameMatcher() {
 	public void testHostnameMatcher() {
 		Tester test = FormatTester.forCode("hostname");
 		Tester test = FormatTester.forCode("hostname");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("google.com")));
 		assertTrue(test.accepts(new TextNode("google.com")));
 		assertFalse(test.accepts(new TextNode("192.168.0.1")));
 		assertFalse(test.accepts(new TextNode("192.168.0.1")));
@@ -59,6 +65,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testipv4Matcher() {
 	public void testipv4Matcher() {
 		Tester test = FormatTester.forCode("ipv4");
 		Tester test = FormatTester.forCode("ipv4");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("192.168.0.1")));
 		assertTrue(test.accepts(new TextNode("192.168.0.1")));
 		assertFalse(test.accepts(new TextNode("192.168.0.1/24")));
 		assertFalse(test.accepts(new TextNode("192.168.0.1/24")));
@@ -68,6 +75,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testipv6Matcher() {
 	public void testipv6Matcher() {
 		Tester test = FormatTester.forCode("ipv6");
 		Tester test = FormatTester.forCode("ipv6");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("::1")));
 		assertTrue(test.accepts(new TextNode("::1")));
 		assertFalse(test.accepts(new TextNode("[::1]:80")));
 		assertFalse(test.accepts(new TextNode("[::1]:80")));
@@ -77,6 +85,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testURIMatcher() {
 	public void testURIMatcher() {
 		Tester test = FormatTester.forCode("uri");
 		Tester test = FormatTester.forCode("uri");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertFalse(test.accepts(new TextNode("#/definitions/schemaArray")));
 		assertFalse(test.accepts(new TextNode("#/definitions/schemaArray")));
@@ -88,6 +97,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testURIReferenceMatcher() {
 	public void testURIReferenceMatcher() {
 		Tester test = FormatTester.forCode("uri-reference");
 		Tester test = FormatTester.forCode("uri-reference");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("#/definitions/schemaArray")));
 		assertTrue(test.accepts(new TextNode("#/definitions/schemaArray")));
@@ -99,6 +109,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testURITemplateeMatcher() {
 	public void testURITemplateeMatcher() {
 		Tester test = FormatTester.forCode("uri-template");
 		Tester test = FormatTester.forCode("uri-template");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("file:///var/log/syslog")));
 		assertTrue(test.accepts(new TextNode("#/definitions/schemaArray")));
 		assertTrue(test.accepts(new TextNode("#/definitions/schemaArray")));
@@ -110,6 +121,7 @@ public class FormatTesterTest {
 	@Test
 	@Test
 	public void testJsonPointerMatcher() {
 	public void testJsonPointerMatcher() {
 		Tester test = FormatTester.forCode("json-pointer");
 		Tester test = FormatTester.forCode("json-pointer");
+		assertThat(test.acceptedTypes(), is(new JsonNodeType[]{JsonNodeType.STRING}));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertFalse(test.accepts(NullNode.getInstance()));
 		assertTrue(test.accepts(new TextNode("/definitions/schemaArray")));
 		assertTrue(test.accepts(new TextNode("/definitions/schemaArray")));
 		assertFalse(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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 
 
 public class ItemsTesterTest {
 public class ItemsTesterTest {
 
 
+	@Test
+	public void testAcceptedTypes() {
+		assertThat(new ItemsTester(FixedTester.ACCEPT).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.ARRAY}));
+	}
+	
 	@Test
 	@Test
 	public void testRejectsNonArray() {
 	public void testRejectsNonArray() {
-		assertFalse(new ItemsTester(Tester.ACCEPT).accepts(NullNode.getInstance()));
+		assertFalse(new ItemsTester(FixedTester.ACCEPT).accepts(NullNode.getInstance()));
 	}
 	}
 
 
 	@Test
 	@Test
 	public void testAcceptsEmptyArray() {
 	public void testAcceptsEmptyArray() {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
-		assertTrue(new ItemsTester(Tester.ACCEPT).accepts(node));
+		assertTrue(new ItemsTester(FixedTester.ACCEPT).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
@@ -28,23 +35,25 @@ public class ItemsTesterTest {
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(false);
 		node.add(false);
 		node.add(true);
 		node.add(true);
-		assertFalse(new ItemsTester(Tester.REJECT).accepts(node));
+		assertFalse(new ItemsTester(FixedTester.REJECT).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
 	public void testAcceptsIfNumTestersPass() {
 	public void testAcceptsIfNumTestersPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(true);
 		node.add(true);
 		node.add(false);
 		node.add(false);
-		assertTrue(new ItemsTester(JsonNode::asBoolean).accepts(node));
+		assertTrue(new ItemsTester(test).accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
 	public void testAcceptsIfNumElemsPass() {
 	public void testAcceptsIfNumElemsPass() {
+		StubTester test = JsonNode::asBoolean;
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
 		node.add(true);
 		node.add(true);
 		node.add(false);
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 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.JsonNode;
 import com.fasterxml.jackson.databind.node.BooleanNode;
 import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 
 public class NotTesterTest {
 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
 	@Test
 	public void testNotInvertsOutputTrue() {
 	public void testNotInvertsOutputTrue() {
-		Tester impl = JsonNode::isNull;
+		StubTester impl = JsonNode::isNull;
 		assertTrue(impl.accepts(NullNode.getInstance()));
 		assertTrue(impl.accepts(NullNode.getInstance()));
 		assertFalse(new NotTester(impl).accepts(NullNode.getInstance()));
 		assertFalse(new NotTester(impl).accepts(NullNode.getInstance()));
 	}
 	}
 
 
 	@Test
 	@Test
 	public void testNotInvertsOutputFalse() {
 	public void testNotInvertsOutputFalse() {
-		Tester impl = JsonNode::isNull;
+		StubTester impl = JsonNode::isNull;
 		assertFalse(impl.accepts(BooleanNode.TRUE));
 		assertFalse(impl.accepts(BooleanNode.TRUE));
 		assertTrue(new NotTester(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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import java.util.function.DoublePredicate;
 import java.util.function.DoublePredicate;
@@ -7,11 +8,18 @@ import java.util.function.DoublePredicate;
 import org.junit.Test;
 import org.junit.Test;
 
 
 import com.fasterxml.jackson.databind.node.DoubleNode;
 import com.fasterxml.jackson.databind.node.DoubleNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 
 public class NumberTesterTest {
 public class NumberTesterTest {
 	DoublePredicate nonZero = d -> d != 0;
 	DoublePredicate nonZero = d -> d != 0;
 	
 	
+	@Test
+	public void testAcceptedTypeIsArgument() {
+		assertThat(new NumberTester(nonZero).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.NUMBER}));
+	}
+	
 	@Test
 	@Test
 	public void testRejectsNonNumber() {
 	public void testRejectsNonNumber() {
 		assertFalse(new NumberTester(nonZero).accepts(NullNode.getInstance()));
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 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.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 
 
 public class OneOfTesterTest {
 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
 	@Test
 	public void testFailsIfAllFail() {
 	public void testFailsIfAllFail() {
 		Tester fails = new OneOfTester(isArray, isObject);
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 
 
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 
 public class PropertyNameTesterTest {
 public class PropertyNameTesterTest {
-
+	@Test
+	public void testAcceptedTypeIsObject() {
+		assertThat(new PropertyNameTester(FixedTester.REJECT).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.OBJECT}));
+	}
+	
 	@Test
 	@Test
 	public void testPassesEmptyObject() {
 	public void testPassesEmptyObject() {
-		PropertyNameTester test = new PropertyNameTester(Tester.REJECT);
+		PropertyNameTester test = new PropertyNameTester(FixedTester.REJECT);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		assertTrue(test.accepts(node));
 		assertTrue(test.accepts(node));
 	}
 	}
 	
 	
 	@Test
 	@Test
 	public void testRejectsIfNameFails() {
 	public void testRejectsIfNameFails() {
-		PropertyNameTester test = new PropertyNameTester(Tester.REJECT);
+		PropertyNameTester test = new PropertyNameTester(FixedTester.REJECT);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		node.set("A", NullNode.getInstance());
 		assertFalse(test.accepts(node));
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
-import org.leumasjaffe.json.schema.Tester;
 import org.leumasjaffe.json.schema.tester.PropertyTester.Pair;
 import org.leumasjaffe.json.schema.tester.PropertyTester.Pair;
 
 
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 
 public class PropertyTesterTest {
 public class PropertyTesterTest {
+	@Test
+	public void testAcceptedTypeIsObject() {
+		assertThat(new PropertyTester(new Pair[0]).acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.OBJECT}));
+	}
 
 
 	@Test
 	@Test
 	public void testPassesUnmatchedProperty() {
 	public void testPassesUnmatchedProperty() {
@@ -22,7 +28,8 @@ public class PropertyTesterTest {
 
 
 	@Test
 	@Test
 	public void testFailsIfMatchFails() {
 	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);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		node.set("A", NullNode.getInstance());
 		assertFalse(test.accepts(node));
 		assertFalse(test.accepts(node));
@@ -31,8 +38,8 @@ public class PropertyTesterTest {
 	@Test
 	@Test
 	public void testFailsIfAnyMatchFails() {
 	public void testFailsIfAnyMatchFails() {
 		PropertyTester test = new PropertyTester(
 		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);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		node.set("A", NullNode.getInstance());
@@ -43,8 +50,8 @@ public class PropertyTesterTest {
 	@Test
 	@Test
 	public void testFailsIfMoreThanOneMatcherForKey() {
 	public void testFailsIfMoreThanOneMatcherForKey() {
 		PropertyTester test = new PropertyTester(
 		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);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		node.set("A", NullNode.getInstance());
@@ -55,8 +62,8 @@ public class PropertyTesterTest {
 	@Test
 	@Test
 	public void testPassesIfAllPass() {
 	public void testPassesIfAllPass() {
 		PropertyTester test = new PropertyTester(
 		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);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		node.set("A", NullNode.getInstance());
@@ -67,8 +74,8 @@ public class PropertyTesterTest {
 	@Test
 	@Test
 	public void testPairMatcherCanOperateOnMultipleKeys() {
 	public void testPairMatcherCanOperateOnMultipleKeys() {
 		PropertyTester test = new PropertyTester(
 		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);
 		final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
 		node.set("A", NullNode.getInstance());
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import java.util.function.IntPredicate;
 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.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 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.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.TextNode;
 import com.fasterxml.jackson.databind.node.TextNode;
@@ -21,6 +23,16 @@ import com.fasterxml.jackson.databind.node.TextNode;
 public class SizeTesterTest {
 public class SizeTesterTest {
 	static IntPredicate NON_ZERO = i -> i > 0;
 	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
 	@Test
 	public void arrayMatcherRejectsObject() {
 	public void arrayMatcherRejectsObject() {
 		final SizeTester notEmptyArray = new SizeTester(ARRAY, NON_ZERO);
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
@@ -33,6 +34,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatcheNullNode() {
 	public void testMatcheNullNode() {
 		final Tester t = TypeTester.fromType("null");
 		final Tester t = TypeTester.fromType("null");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertTrue(t.accepts(jNull));
 		assertTrue(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
 		assertFalse(t.accepts(integral));
@@ -45,6 +47,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatchesBooleanNode() {
 	public void testMatchesBooleanNode() {
 		final Tester t = TypeTester.fromType("boolean");
 		final Tester t = TypeTester.fromType("boolean");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(jNull));
 		assertTrue(t.accepts(bool));
 		assertTrue(t.accepts(bool));
 		assertFalse(t.accepts(integral));
 		assertFalse(t.accepts(integral));
@@ -57,6 +60,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatchesIntegerNode() {
 	public void testMatchesIntegerNode() {
 		final Tester t = TypeTester.fromType("integer");
 		final Tester t = TypeTester.fromType("integer");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(bool));
 		assertTrue(t.accepts(integral));
 		assertTrue(t.accepts(integral));
@@ -69,6 +73,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatchesDoubleNode() {
 	public void testMatchesDoubleNode() {
 		final Tester t = TypeTester.fromType("number");
 		final Tester t = TypeTester.fromType("number");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(bool));
 		assertTrue(t.accepts(integral));
 		assertTrue(t.accepts(integral));
@@ -81,6 +86,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatchesTextNode() {
 	public void testMatchesTextNode() {
 		final Tester t = TypeTester.fromType("string");
 		final Tester t = TypeTester.fromType("string");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
 		assertFalse(t.accepts(integral));
@@ -93,6 +99,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatchesArrayNode() {
 	public void testMatchesArrayNode() {
 		final Tester t = TypeTester.fromType("array");
 		final Tester t = TypeTester.fromType("array");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
 		assertFalse(t.accepts(integral));
@@ -105,6 +112,7 @@ public class TypeTesterTest {
 	@Test
 	@Test
 	public void testMatchesObjectNode() {
 	public void testMatchesObjectNode() {
 		final Tester t = TypeTester.fromType("object");
 		final Tester t = TypeTester.fromType("object");
+		assertThat(t.acceptedTypes(), is(Tester.ANY));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(jNull));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(bool));
 		assertFalse(t.accepts(integral));
 		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;
 package org.leumasjaffe.json.schema.tester;
 
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
 import org.junit.Test;
 import org.junit.Test;
 
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 
 public class UniqueItemTesterTest {
 public class UniqueItemTesterTest {
+	
+	@Test
+	public void testAcceptedTypeIsArray() {
+		assertThat(new UniqueItemTester().acceptedTypes(),
+				is(new JsonNodeType[]{JsonNodeType.ARRAY}));
+	}
 
 
 	@Test
 	@Test
 	public void testAcceptsEmptyArray() {
 	public void testAcceptsEmptyArray() {