com.zuunr.json.schema.validation.node.ValidationNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of json Show documentation
Show all versions of json Show documentation
Immutable JSON representation in Java
package com.zuunr.json.schema.validation.node;
import com.zuunr.json.JsonArray;
import com.zuunr.json.JsonObject;
import com.zuunr.json.JsonObjectBuilder;
import com.zuunr.json.JsonValue;
import com.zuunr.json.pointer.JsonPointer;
import com.zuunr.json.schema.JsonSchema;
import com.zuunr.json.schema.validation.OutputStructure;
import com.zuunr.json.schema.validation.ValidationContext;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* @author Niklas Eldberger
*/
public abstract class ValidationNode {
private static final JsonObject VALID_FLAG = JsonObject.EMPTY.put("valid", true);
private static final JsonObject INVALID_FLAG = JsonObject.EMPTY.put("valid", false);
private final ValidationContext validationContext;
protected final JsonValue rootInstance;
protected final JsonValue instance;
protected JsonValue filtrate;
public final JsonSchema schema;
protected ValidationNode parentNode;
private boolean childNodesCompleted;
public Location location;
private Boolean valid;
private JsonObject error;
private ValidationNode nextSiblingNode;
private ValidationNode firstChildNode;
private int schemaKeywordIndex = -1;
public ValidationNode(JsonValue instance, JsonSchema schema, int schemaKeywordIndex, ValidationContext validationContext, JsonValue rootInstance) {
this.instance = instance;
this.schema = schema;
this.validationContext = validationContext;
this.rootInstance = rootInstance;
this.schemaKeywordIndex = schemaKeywordIndex;
}
public int keywordSchemaIndex() {
return schemaKeywordIndex;
}
protected void childNodeCompleted(ValidationNode subnode) {
}
final void allChildNodesAreCompleted() {
childNodesCompleted = true;
doAfterAllChildNodesAreCompleted();
}
/**
* A handle that is called when all subnodes are completed. If valid
is not already set this method is
* now able to do that.
*/
protected abstract void doAfterAllChildNodesAreCompleted();
public final boolean resultIsKnown() {
return investigateIfValid() != null;
}
public final Boolean investigateIfValid() {
if (valid == null) {
valid = calculateValid();
}
return valid;
}
protected final void setValid(boolean valid) {
if (this.valid != null && !this.valid.equals(valid)) {
throw new RuntimeException("Valid is already set!");
}
this.valid = valid;
}
/**
* Is called by getValidationResult() if there i no ValidationResult available yet.
*
* @return a ValidationResult if it can be calculated, otherwise null.
*/
protected abstract Boolean calculateValid();
protected ValidationNode firstChildNode() {
if (firstChildNode == null) {
firstChildNode = createFirstChildNode();
}
return firstChildNode;
}
/**
* Creates the first nested subnode. This class is the parentnode of the childnode and the childnode is typically of
* another class than this parentnode.
*
* @return the first childnode of this ValidationNode or null if there is no childnode
*/
protected abstract ValidationNode createFirstChildNode();
protected ValidationNode nextSiblingNode() {
if (nextSiblingNode == null) {
nextSiblingNode = createNextChildNodeOfParent();
}
return nextSiblingNode;
}
private ValidationNode nextErrorSiblingNode() {
ValidationNode next = nextSiblingNode;
if (next == null) {
return null;
}
while (next.getValid() || validationContext().outputStructure() == OutputStructure.VERBOSE) {
next = next.nextSiblingNode;
if (next == null) {
return null;
}
}
return next;
}
/**
* Creates the next childnode of the parentnode. The returned ValidationStep is typically of the same class as this ValidationStep
*
* @return
*/
protected abstract ValidationNode createNextChildNodeOfParent();
final ValidationNode parentNode() {
return parentNode;
}
boolean isChildNodesCompleted() {
return childNodesCompleted;
}
public JsonValue filtrate() {
if (filtrate == null) {
if (investigateIfValid()) {
filtrate = instance;
}
}
return filtrate;
}
public ValidationContext validationContext() {
return validationContext;
}
void cleanCompletedChildNodesList() {
}
public abstract Location location();
public Boolean getValid() {
return valid;
}
public JsonObject validationResult() {
return buildValidationResult(this, validationContext);
}
//TODO: Refactor this method
static JsonObject buildValidationResult(ValidationNode schemaNode, ValidationContext validationContext) {
if (schemaNode.getValid()) {
return VALID_FLAG;
}
if (validationContext.outputStructure() == OutputStructure.FLAG) {
return INVALID_FLAG;
}
Deque stack = new ArrayDeque();
ValidationNode top = schemaNode;
top.location();
stack.push(schemaNode);
while (!stack.isEmpty()) {
top = stack.peek();
if (top.firstChildNode == null) {
top.location();
top.error(buildError(top, null));
stack.pop();
} else {
JsonArray nestedErrorsBuilder = null; // TODO This should be a builder
ValidationNode errorSubnode = top.firstChildNode();
boolean allErrorsAboveInStackHandled = true;// = false;
while (errorSubnode != null) {
if (errorSubnode.error() == null) {
if (OutputStructure.VERBOSE == validationContext.outputStructure() || !errorSubnode.getValid()) {
errorSubnode.location();
stack.push(errorSubnode);
allErrorsAboveInStackHandled = false;
}
} else {
int errorsSize = errorSubnode.error().get("errors", JsonArray.EMPTY).getJsonArray().size();
if (errorsSize == 1 && OutputStructure.VERBOSE != validationContext.outputStructure()) {
if (nestedErrorsBuilder == null) {
nestedErrorsBuilder = JsonArray.EMPTY;
}
nestedErrorsBuilder = nestedErrorsBuilder.add(errorSubnode.error().get("errors").get(0));
} else {
if (nestedErrorsBuilder == null) {
nestedErrorsBuilder = JsonArray.EMPTY;
}
nestedErrorsBuilder = nestedErrorsBuilder.add(errorSubnode.error());
}
}
errorSubnode = errorSubnode.nextErrorSiblingNode();
}
top.location();
top.error(buildError(top, nestedErrorsBuilder));
if (allErrorsAboveInStackHandled) {
stack.pop();
}
}
}
return top.error();
}
private static JsonObject buildError(ValidationNode validationNode, JsonArray nestedErrors) {
JsonObjectBuilder errorBuilder = JsonObject.EMPTY.builder();
errorBuilder
.put("instanceLocation", validationNode.location.instance.as(JsonPointer.class).getJsonPointerString())
.put("keywordLocation", validationNode.location.keyword.as(JsonPointer.class).getJsonPointerString())
.put("valid", validationNode.valid);
if (validationNode.location.instanceProperty != null) {
errorBuilder = errorBuilder.put("instanceProperty", validationNode.location.instanceProperty);
}
if (validationNode.location.keywordValue != null) {
errorBuilder = errorBuilder.put("keywordValue", validationNode.location.keywordValue);
}
if (nestedErrors != null) {
errorBuilder = errorBuilder.put("errors", nestedErrors);
}
return errorBuilder.build();
}
private JsonObject error() {
return error;
}
private void error(JsonObject error) {
this.error = error;
}
protected abstract JsonValue keyword();
public static class Location {
public JsonArray instance;
public String instanceProperty;
public JsonArray keyword;
public JsonValue keywordValue;
public Location() {
}
public Location(JsonArray instance, String instanceProperty, JsonArray keyword, JsonValue keywordValue) {
this.instance = instance;
this.instanceProperty = instanceProperty;
this.keyword = keyword;
this.keywordValue = keywordValue;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy