org.etlunit.json.validator.JsonSchemaObjectNode Maven / Gradle / Ivy
package org.etlunit.json.validator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class JsonSchemaObjectNode {
private static final Map typesByName = new HashMap();
private final ObjectNode sourceNode;
public static enum valid_type
{
t_string,
t_number,
t_integer,
t_boolean,
t_object,
t_array,
t_null,
t_any;
private valid_type()
{
typesByName.put(name().substring(2), this);
}
}
static
{
// silly little Java thing to make sure this class is loaded
valid_type.t_string.ordinal();
}
private final List type = new ArrayList();
private final boolean required;
private final boolean uniqueItems;
private final Double maximum;
private final Double minimum;
private final Double exclusiveMaximum;
private final Double exclusiveMinimum;
private final Integer maxItems;
private final Integer minItems;
private final Integer minLength;
private final Integer maxLength;
private final String id;
private final String _ref;
private final String _schema;
private final String title;
private final String description;
private final String format;
private final String defaultValue;
private final List enumValues;
private final Pattern pattern;
private final Double divisibleBy;
private final Map propertySchemas = new HashMap();
private final Map patternPropertySchemas = new HashMap();
private boolean arrayItems = false;
private final List items = new ArrayList();
private final List exclusive;
private boolean exclusiveRequired;
private final List _extends = new ArrayList();
private final boolean allowsAdditionalProperties;
private final JsonSchemaObjectNode additionalProperties;
private final boolean allowsAdditionalItems;
private final JsonSchemaObjectNode additionalItems;
JsonSchemaObjectNode(ObjectNode node) throws JsonSchemaValidationException
{
sourceNode = node;
required = readBoolean(node, "required", false);
readTypeNode(node);
uniqueItems = readBoolean(node, "uniqueItems", false);
exclusive = readExclusive(node);
maximum = readBigDecimal(node, "maximum", null);
minimum = readBigDecimal(node, "minimum", null);
exclusiveMaximum = readBigDecimal(node, "exclusiveMaximum", null);
exclusiveMinimum = readBigDecimal(node, "exclusiveMinimum", null);
divisibleBy = readBigDecimal(node, "divisibleBy", null);
if (divisibleBy != null && divisibleBy.doubleValue() <= 0)
{
throw new JsonSchemaValidationException("Invalid divisibleBy property - value must be > 0", "", node, null);
}
minItems = readArrayInteger(node, "minItems", new Integer(0));
if (minItems != null && minItems.intValue() < 0)
{
throw new JsonSchemaValidationException("Invalid minItems property - value must be >= 0", "", node, null);
}
maxItems = readArrayInteger(node, "maxItems", null);
minLength = readInteger(node, "minLength", new Integer(0));
if (minLength != null && minLength.intValue() < 0)
{
throw new JsonSchemaValidationException("Invalid minLength property - value must be >= 0", "", node, null);
}
maxLength = readInteger(node, "maxLength", null);
title = readString(node, "title");
description = readString(node, "description");
format = readString(node, "format");
id = readString(node, "id");
_ref = readString(node, "$ref");
_schema = readString(node, "$schema");
enumValues = readEnum(node);
readExtends(node);
pattern = readPattern(node);
defaultValue = readDefault(node);
readProperties(node);
readPatternProperties(node);
readItems(node);
Object add = readAdditionalProperties(node, "additionalProperties");
if (add instanceof Boolean)
{
allowsAdditionalProperties = ((Boolean) add).booleanValue();
additionalProperties = null;
}
else
{
allowsAdditionalProperties = true;
additionalProperties = (JsonSchemaObjectNode) add;
}
add = readAdditionalProperties(node, "additionalItems");
if (add instanceof Boolean)
{
allowsAdditionalItems = ((Boolean) add).booleanValue();
additionalItems = null;
}
else
{
allowsAdditionalItems = true;
additionalItems = (JsonSchemaObjectNode) add;
}
}
private Object readAdditionalProperties(ObjectNode node, String propertyName) throws JsonSchemaValidationException {
JsonNode defNode = node.get(propertyName);
if (defNode == null)
{
return Boolean.TRUE;
}
else if (defNode.isBoolean())
{
return new Boolean(defNode.asBoolean());
}
else if (defNode.isObject())
{
return new JsonSchemaObjectNode((ObjectNode) defNode);
}
else
{
throw new JsonSchemaValidationException("Invalid additionalProperties property - not object or boolean type", "", defNode, null);
}
}
private String readDefault(ObjectNode node) {
JsonNode defNode = node.get("default");
if (defNode != null)
{
return defNode.toString();
}
return null;
}
private List readExclusive(ObjectNode node) throws JsonSchemaValidationException {
JsonNode propsNode = node.get("exclusive");
if (propsNode == null)
{
return null;
}
List enode = new ArrayList();
if (!propsNode.isObject())
{
throw new JsonSchemaValidationException("Invalid exclusive property - not object type", "", propsNode, null);
}
else
{
Map attMap = new HashMap();
attMap.put("items", Boolean.TRUE);
attMap.put("required", Boolean.FALSE);
validatePropertySet((ObjectNode) propsNode, attMap, propsNode);
JsonNode required1 = propsNode.get("required");
if (required1 != null)
{
exclusiveRequired = required1.asBoolean();
}
propsNode = propsNode.get("items");
for (JsonNode inode : propsNode)
{
if (!inode.isObject())
{
throw new JsonSchemaValidationException("Invalid exclusive schema entry - not object type", "", inode, null);
}
enode.add(loadSchemaItem((ObjectNode) inode));
}
}
return enode;
}
private void readItems(ObjectNode node) throws JsonSchemaValidationException {
JsonNode propsNode = node.get("items");
if (propsNode == null)
{
return;
}
if (propsNode.isObject())
{
addItem((ObjectNode) propsNode);
}
else if (propsNode.isArray())
{
arrayItems = true;
for (JsonNode inode : propsNode)
{
if (!inode.isObject())
{
throw new JsonSchemaValidationException("Invalid items entry - not object type", "", inode, null);
}
addItem((ObjectNode) inode);
}
}
else
{
throw new JsonSchemaValidationException("Invalid items property - not object or array type", "", propsNode, null);
}
}
private JsonSchemaObjectNode loadSchemaItem(ObjectNode inode) throws JsonSchemaValidationException {
return new JsonSchemaObjectNode(inode);
}
private void addItem(ObjectNode inode) throws JsonSchemaValidationException {
items.add(loadSchemaItem(inode));
}
private void readProperties(ObjectNode node) throws JsonSchemaValidationException {
JsonNode propsNode = node.get("properties");
if (propsNode == null)
{
return;
}
if (!propsNode.isObject())
{
throw new JsonSchemaValidationException("Invalid properties property - not object type", "", propsNode, null);
}
Iterator> fields = propsNode.getFields();
while (fields.hasNext())
{
Map.Entry field = fields.next();
if (propertySchemas.containsKey(field.getKey()))
{
throw new JsonSchemaValidationException("Invalid properties property - property specified twice: " + field, "", propsNode, null);
}
JsonNode value = field.getValue();
if (!value.isObject())
{
throw new JsonSchemaValidationException("Invalid properties property - value type not object: " + value, "", propsNode, null);
}
propertySchemas.put(field.getKey(), new JsonSchemaObjectNode((ObjectNode) value));
}
}
private void readPatternProperties(ObjectNode node) throws JsonSchemaValidationException {
JsonNode propsNode = node.get("patternProperties");
if (propsNode == null)
{
return;
}
if (!propsNode.isObject())
{
throw new JsonSchemaValidationException("Invalid patternProperties property - not object type", "", propsNode, null);
}
Iterator> fields = propsNode.getFields();
while (fields.hasNext())
{
Map.Entry field = fields.next();
if (patternPropertySchemas.containsKey(field.getKey()))
{
throw new JsonSchemaValidationException("Invalid patternProperties property - pattern specified twice: " + field, "", propsNode, null);
}
JsonNode value = field.getValue();
if (!value.isObject())
{
throw new JsonSchemaValidationException("Invalid patternProperties property - value type not object: " + value, "", propsNode, null);
}
try
{
patternPropertySchemas.put(Pattern.compile(field.getKey()), new JsonSchemaObjectNode((ObjectNode) value));
}
catch(PatternSyntaxException exc)
{
throw new JsonSchemaValidationException("Invalid regular expression pattern: " + field.getKey(), "", propsNode, null);
}
}
}
private Pattern readPattern(ObjectNode node) throws JsonSchemaValidationException {
String text = readString(node, "pattern");
if (text == null)
{
return null;
}
try
{
return Pattern.compile(text);
}
catch(PatternSyntaxException exc)
{
throw new JsonSchemaValidationException("Invalid regular expression pattern: " + text, "", node, null);
}
}
private List readEnum(ObjectNode node) throws JsonSchemaValidationException {
JsonNode reqNode = node.get("enum");
if (reqNode == null)
{
return null;
}
List list = new ArrayList();
ArrayNode anode = (ArrayNode) reqNode;
if (anode.size() == 0)
{
throw new JsonSchemaValidationException("Invalid enum - at least one value must be specified: " + anode, "", anode, null);
}
for (JsonNode enode : anode)
{
if (!enode.isTextual())
{
throw new JsonSchemaValidationException("Invalid enum value type - must be a string: " + enode, "", enode, null);
}
String text = enode.asText();
if (list.contains(text))
{
throw new JsonSchemaValidationException("Invalid enum - value duplicated: " + enode, "", anode, null);
}
list.add(text);
}
return list;
}
private void readExtends(ObjectNode node) throws JsonSchemaValidationException {
JsonNode reqNode = node.get("extends");
if (reqNode == null)
{
return;
}
if (reqNode.isTextual())
{
_extends.add(reqNode.asText());
}
else if (reqNode.isArray())
{
ArrayNode anode = (ArrayNode) reqNode;
for (JsonNode enode : anode)
{
if (!enode.isTextual())
{
throw new JsonSchemaValidationException("Invalid extends value type - must be a string: " + enode, "", enode, null);
}
String text = enode.asText();
if (_extends.contains(text))
{
throw new JsonSchemaValidationException("Invalid extends - value duplicated: " + enode, "", anode, null);
}
_extends.add(text);
}
}
else
{
throw new JsonSchemaValidationException("Invalid extends - node wrong type", "", reqNode, null);
}
}
private void validatePropertySet(ObjectNode node, Map attMap, JsonNode jnode) throws JsonSchemaValidationException
{
// check for extra nodes
Iterator attrit = node.getFieldNames();
while (attrit.hasNext())
{
String str = attrit.next();
if (!attMap.containsKey(str))
{
throw new JsonSchemaValidationException("Invalid schema property: " + str, "", jnode, null);
}
}
}
private Integer readInteger(ObjectNode node, String property, Integer default_) throws JsonSchemaValidationException {
JsonNode reqNode = node.get(property);
if (reqNode == null)
{
return default_;
}
// check that this object is defining a number type
if (type == null)
{
throw new JsonSchemaValidationException("Cannot specify property " + property + " on an unspecified type node: " + node, "", reqNode, null);
}
if (
type.contains(valid_type.t_string)
||
type.contains(valid_type.t_any)
||
type.contains(valid_type.t_array)
||
type.contains(valid_type.t_boolean)
||
type.contains(valid_type.t_object)
)
{
throw new JsonSchemaValidationException("Cannot specify property " + property + " on a non-number type", "", node, null);
}
if (!reqNode.isInt())
{
throw new JsonSchemaValidationException(property + " property must be an integer: " + node.toString(), "", reqNode, null);
}
return reqNode.asInt();
}
private Integer readArrayInteger(ObjectNode node, String property, Integer default_) throws JsonSchemaValidationException {
JsonNode reqNode = node.get(property);
if (reqNode == null)
{
return default_;
}
// check that this object is defining a number type
if (type == null)
{
throw new JsonSchemaValidationException("Cannot specify property " + property + " on an unspecified type node", "", reqNode, null);
}
if (!reqNode.isInt())
{
throw new JsonSchemaValidationException(property + " property must be an integer: " + node.toString(), "", reqNode, null);
}
return reqNode.asInt();
}
private Double readBigDecimal(ObjectNode node, String property, Double default_) throws JsonSchemaValidationException {
JsonNode reqNode = node.get(property);
if (reqNode == null)
{
return default_;
}
// check that this object is defining a number type
if (!reqNode.isNumber() && !reqNode.isIntegralNumber())
{
throw new JsonSchemaValidationException("Cannot specify property " + property + " on a non-number type", "", reqNode, null);
}
if (!reqNode.isNumber())
{
throw new JsonSchemaValidationException(property + " property must be an integer", "", reqNode, null);
}
return new Double(reqNode.asDouble());
}
private boolean readBoolean(ObjectNode node, String property, boolean default_) throws JsonSchemaValidationException {
JsonNode reqNode = node.get(property);
if (reqNode == null)
{
return default_;
}
if (!reqNode.isBoolean())
{
throw new JsonSchemaValidationException(property + " property must be a boolean", "", reqNode, null);
}
return reqNode.asBoolean();
}
private String readString(ObjectNode node, String property) throws JsonSchemaValidationException {
JsonNode reqNode = node.get(property);
if (reqNode == null)
{
return null;
}
if (!reqNode.isTextual())
{
throw new JsonSchemaValidationException(property + " property must be a string", "", reqNode, null);
}
return reqNode.asText();
}
private void readTypeNode(ObjectNode node) throws JsonSchemaValidationException {
JsonNode typeNode = node.get("type");
if (typeNode != null)
{
if (typeNode.isArray())
{
Iterator it = ((ArrayNode) typeNode).getElements();
while (it.hasNext())
{
JsonNode vnode = it.next();
if (!vnode.isTextual())
{
throw new JsonSchemaValidationException("Invalid type union. Item is not textual", "", typeNode, null);
}
addTypeValue(vnode.asText());
}
}
else if (typeNode.isTextual())
{
addTypeValue(typeNode.asText());
}
else
{
throw new JsonSchemaValidationException("Invalid schema - invalid object type", "", typeNode, null);
}
}
}
private void addTypeValue(String s) throws JsonSchemaValidationException {
valid_type vvalid_type = typesByName.get(s);
if (type.contains(vvalid_type))
{
throw new JsonSchemaValidationException("Type " + s + " specified twice in union", "", null, null);
}
if (vvalid_type != null)
{
type.add(vvalid_type);
}
else
{
throw new JsonSchemaValidationException("Invalid object type: " + s, "", null, null);
}
}
public List getType() {
return type;
}
public boolean isRequired()
{
return required;
}
public boolean isUniqueItems() {
return uniqueItems;
}
public Double getMaximum() {
return maximum;
}
public Double getMinimum() {
return minimum;
}
public Double getExclusiveMaximum() {
return exclusiveMaximum;
}
public Double getExclusiveMinimum() {
return exclusiveMinimum;
}
public Integer getMaxItems() {
return maxItems;
}
public Integer getMaxLength() {
return maxLength;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getFormat() {
return format;
}
public List getEnumValues() {
return enumValues;
}
public Pattern getPattern() {
return pattern;
}
public Integer getMinLength() {
return minLength;
}
public Integer getMinItems() {
return minItems;
}
public Double getDivisibleBy() {
return divisibleBy;
}
public Map getPropertySchemas() {
return propertySchemas;
}
public List getItems() {
return items;
}
public String getDefaultValue() {
return defaultValue;
}
public Map getPatternPropertySchemas() {
return patternPropertySchemas;
}
public boolean allowsAdditionalProperties() {
return allowsAdditionalProperties;
}
public boolean allowsAdditionalItems() {
return allowsAdditionalItems;
}
public JsonSchemaObjectNode getAdditionalItems() {
return additionalItems;
}
public JsonSchemaObjectNode getAdditionalProperties() {
return additionalProperties;
}
public String getId() {
return id;
}
public String getRef() {
return _ref;
}
public String getSchema() {
return _schema;
}
public boolean isArrayItems() {
return arrayItems;
}
@Override
public String toString() {
return "JsonSchemaObjectNode{" +
"sourceNode=" + sourceNode +
'}';
}
public List getExtends() {
return _extends;
}
public ObjectNode getSourceNode() {
return sourceNode;
}
public List getExclusive()
{
return exclusive;
}
public boolean isExclusiveRequired()
{
return exclusiveRequired;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy