
com.github.fge.jsonschema.tree.InlineSchemaTree 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.github.fge.jsonschema.load.Dereferencing;
import com.github.fge.jsonschema.ref.JsonPointer;
import com.github.fge.jsonschema.ref.JsonRef;
import com.github.fge.jsonschema.util.JacksonUtils;
import com.google.common.collect.Maps;
import java.util.Map;
public final class InlineSchemaTree
extends BaseSchemaTree
{
/**
* The list of contexts whose URIs bear a JSON Pointer as a fragment part
*/
private final Map ptrRefs = Maps.newHashMap();
/**
* The list of contexts whose URIs bear a non JSON Pointer fragment part
*/
private final Map otherRefs = Maps.newHashMap();
public InlineSchemaTree(final JsonNode baseNode)
{
this(JsonRef.emptyRef(), baseNode);
}
public InlineSchemaTree(final JsonRef loadingRef, final JsonNode baseNode)
{
this(loadingRef, baseNode, JsonPointer.empty(), false);
}
private InlineSchemaTree(final JsonRef loadingRef, final JsonNode baseNode,
final JsonPointer pointer, final boolean valid)
{
super(loadingRef, baseNode, pointer, Dereferencing.INLINE, valid);
walk(startingRef, baseNode, JsonPointer.empty(), ptrRefs, otherRefs);
}
@Override
public SchemaTree append(final JsonPointer pointer)
{
return new InlineSchemaTree(loadingRef, baseNode,
this.pointer.append(pointer), valid);
}
@Override
public SchemaTree setPointer(final JsonPointer pointer)
{
return new InlineSchemaTree(loadingRef, baseNode, pointer, valid);
}
@Override
public boolean containsRef(final JsonRef ref)
{
return getMatchingPointer(ref) != null;
}
@Override
public JsonPointer matchingPointer(final JsonRef ref)
{
final JsonPointer ret = getMatchingPointer(ref);
if (ret == null)
return null;
return ret.resolve(baseNode).isMissingNode() ? null : ret;
}
@Override
public SchemaTree withValidationStatus(final boolean valid)
{
return new InlineSchemaTree(loadingRef, baseNode, pointer, valid);
}
private JsonPointer getMatchingPointer(final JsonRef ref)
{
return ref.getFragment().isPointer()
? refMatchingPointer(ref)
: otherMatchingPointer(ref);
}
/**
* Return a matching pointer for a JSON Pointer terminated fully resolved
* reference
*
* This includes the loading URI. Note, however, that due to "id"
* intricacies, the test against the loading reference is done only as a
* last resort.
*
* @param ref the target reference
* @return the matching pointer, or {@code null} if not found
*/
private JsonPointer refMatchingPointer(final JsonRef ref)
{
final JsonPointer refPtr = (JsonPointer) ref.getFragment();
/*
* When using inline addressing, we must favor whatever "id" has defined
* as a URI scope over what the loading URI is...
*/
JsonRef inlineRef;
JsonPointer inlinePtr;
for (final Map.Entry entry: ptrRefs.entrySet()) {
inlineRef = entry.getKey();
if (!entry.getKey().contains(ref))
continue;
inlinePtr = (JsonPointer) inlineRef.getFragment();
if (!inlinePtr.isParentOf(refPtr))
continue;
return entry.getValue().append(inlinePtr.relativize(refPtr));
}
/*
* ... Which means this test must be done last... (since refPtr is
* declared final, this is safe)
*/
return loadingRef.contains(ref) ? refPtr : null;
}
/**
* Return a matching pointer for a non JSON Pointer terminated, fully
* resolved reference
*
* This simply tries and retrieves a value from {@link #otherRefs},
* since an exact matching is required in such a case.
*
* @param ref the target reference
* @return the matching pointer, or {@code null} if not found
*/
private JsonPointer otherMatchingPointer(final JsonRef ref)
{
return otherRefs.get(ref);
}
/**
* Walk a JSON document to collect URI contexts
*
* Unlike what happens with a canonical schema tree, we must walk
* the whole tree in advance here. This is necessary for {@link
* #containsRef(JsonRef)} and {@link #matchingPointer(JsonRef)} to work.
*
* This method is called recursively. Its furst invocation is from the
* constructor, with {@link #loadingRef} as a reference, {@link #baseNode}
* as a JSON document and an empty pointer as the document pointer.
*
* @param baseRef the current context
* @param node the current document
* @param ptr the current pointer into the base document
* @param ptrMap a "JSON Pointer context" map to fill
* @param otherMap a non JSON Pointer context map to fill
*
* @see #idFromNode(JsonNode)
*/
private static void walk(final JsonRef baseRef, final JsonNode node,
final JsonPointer ptr, final Map ptrMap,
final Map otherMap)
{
/*
* FIXME: this means we won't go through schemas in keywords such as
* "anyOf" and friends. No idea whether this is a concern. It may be.
*/
if (!node.isObject())
return;
final JsonRef ref = idFromNode(node);
final Map targetMap;
JsonRef nextRef = baseRef;
if (ref != null) {
nextRef = baseRef.resolve(ref);
targetMap = nextRef.getFragment().isPointer() ? ptrMap : otherMap;
targetMap.put(nextRef, ptr);
}
final Map tmp = JacksonUtils.asMap(node);
for (final Map.Entry entry: tmp.entrySet())
walk(nextRef, entry.getValue(), ptr.append(entry.getKey()), ptrMap,
otherMap);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy