![JAR search and dependency download from the Maven repository](/logo.png)
com.github.fge.jsonschema.tree.JsonSchemaTree Maven / Gradle / Ivy
/*
* Copyright (c) 2013, Francis Galiegue
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Lesser GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.github.fge.jsonschema.tree;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.fge.jsonschema.main.JsonSchemaException;
import com.github.fge.jsonschema.processing.ref.Dereferencing;
import com.github.fge.jsonschema.ref.JsonPointer;
import com.github.fge.jsonschema.ref.JsonRef;
import com.google.common.collect.Queues;
import java.util.Deque;
/**
* A {@link JsonTree} carrying URI resolution context information
*
* In addition to what {@link JsonTree} does, this tree also modifies URI
* resolution context information when changing paths, and adds methods in order
* to query this resolution context.
*
* All context information is carried as JSON References, since this is what
* is used for addressing in JSON Schema.
*
* @see JsonRef
* @see CanonicalSchemaTree
* @see InlineSchemaTree
*/
public abstract class JsonSchemaTree
extends BaseJsonTree
{
/**
* Whether inline dereferencing is used
*/
private final Dereferencing dereferencing;
/**
* The JSON Reference from which this node has been loaded
*
* If loaded without a URI, this will be the empty reference.
*/
protected final JsonRef loadingRef;
/**
* The JSON Reference representing the context at the root of the schema
*
* It will defer from {@link #loadingRef} if there is an {@code id} at
* the top level.
*/
protected final JsonRef startingRef;
/**
* The stack of resolution contexts
*/
protected final Deque refStack = Queues.newArrayDeque();
/**
* The current resolution context
*/
protected JsonRef currentRef;
/**
* The main constructor
*
* @param loadingRef the loading reference
* @param baseNode the base node
*/
protected JsonSchemaTree(final JsonRef loadingRef, final JsonNode baseNode,
final Dereferencing dereferencing)
{
super(baseNode);
this.dereferencing = dereferencing;
this.loadingRef = currentRef = loadingRef;
final JsonRef ref = idFromNode(baseNode);
if (ref != null)
currentRef = currentRef.resolve(ref);
startingRef = currentRef;
}
@Override
public final void append(final JsonPointer ptr)
{
refStack.push(currentRef);
currentRef = nextRef(currentRef, ptr.asElements(), currentNode);
pushPointer(currentPointer.append(ptr));
pushNode(ptr.resolve(currentNode));
}
@Override
public final void pop()
{
currentRef = refStack.pop();
popPointer();
popNode();
}
public final void setPointer(final JsonPointer pointer)
{
refStack.push(currentRef);
currentRef = nextRef(startingRef, pointer.asElements(), baseNode);
pushPointer(pointer);
pushNode(pointer.resolve(baseNode));
}
/**
* Resolve a JSON Reference against the current resolution context
*
* @param other the JSON Reference to resolve
* @return the resolved reference
* @see JsonRef#resolve(JsonRef)
*/
public final JsonRef resolve(final JsonRef other)
{
return currentRef.resolve(other);
}
/**
* Tell whether a JSON Reference is contained within this schema tree
*
* This method will return {@code true} if the caller can attempt
* to retrieve the JSON value addressed by this reference from the schema
* tree directly.
*
* Note that the reference must be fully resolved for this method
* to work.
*
* @param ref the target reference
* @return see description
* @see #resolve(JsonRef)
*/
public abstract boolean containsRef(final JsonRef ref);
/**
* Return a matching pointer in this tree for a fully resolved reference
*
* This must be called only when {@link #containsRef(JsonRef)}
* returns {@code true}. Otherwise, its result is undefined.
*
* @param ref the reference
* @return the matching pointer, or {@code null} if not found
*/
public abstract JsonPointer matchingPointer(final JsonRef ref);
/**
* Get the loading URI for that schema
*
* @return the loading URI as a {@link JsonRef}
*/
public final JsonRef getLoadingRef()
{
return loadingRef;
}
/**
* Get the current resolution context
*
* @return the context as a {@link JsonRef}
*/
public final JsonRef getCurrentRef()
{
return currentRef;
}
/**
* Return a copy of this tree at its current state but with an empty stack
*
* @return the copy
*/
public final JsonSchemaTree copy()
{
final JsonSchemaTree ret = dereferencing.newTree(loadingRef, baseNode);
ret.currentRef = currentRef;
ret.currentPointer = currentPointer;
ret.currentNode = currentNode;
return ret;
}
/**
* Build a JSON Reference from a node
*
* This will return {@code null} if the reference could not be built. The
* conditions for a successful build are as follows:
*
*
* - the node is an object;
* - it has a member named {@code id};
* - the value of this member is a string;
* - this string is a valid URI.
*
*
* @param node the node
* @return a JSON Reference, or {@code null}
*/
protected static JsonRef idFromNode(final JsonNode node)
{
if (!node.path("id").isTextual())
return null;
try {
return JsonRef.fromString(node.get("id").textValue());
} catch (JsonSchemaException ignored) {
return null;
}
}
/**
* Calculate the next URI context from a starting reference and node
*
* @param startingRef the starting reference
* @param pointers the list of JSON Pointers
* @param startingNode the starting node
* @return the calculated reference
*/
private static JsonRef nextRef(final JsonRef startingRef,
final Iterable pointers, final JsonNode startingNode)
{
JsonRef ret = startingRef;
JsonRef idRef;
JsonNode node = startingNode;
for (final JsonPointer pointer: pointers) {
node = pointer.resolve(node);
idRef = idFromNode(node);
if (idRef != null)
ret = ret.resolve(idRef);
}
return ret;
}
@Override
public final JsonNode asJson()
{
final ObjectNode ret = FACTORY.objectNode();
ret.put("loadingURI", FACTORY.textNode(loadingRef.toString()));
ret.put("pointer", FACTORY.textNode(currentPointer.toString()));
ret.put("currentContext", FACTORY.textNode(currentRef.toString()));
ret.put("dereferencing", FACTORY.textNode(dereferencing.toString()));
return ret;
}
@Override
public final String toString()
{
return asJson().toString();
}
}