All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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.processing.ref.Dereferencing;
import com.github.fge.jsonschema.ref.JsonPointer;
import com.github.fge.jsonschema.ref.JsonRef;
import com.github.fge.jsonschema.util.jackson.JacksonUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import java.util.Map;

/**
 * A {@link JsonSchemaTree} using inline dereferencing
 *
 * 

When using inline dereferencing, a (fully resolved) reference is contained * within the tree if it is relative to one of the URI resolution contexts in * this tree, or to the loading URI itself.

* *

And this makes things quite hairy to handle. Consider, for instance, that * {@code foo://bar#/a/b/c} is contained within such a tree if the tree has a * resolution context with URI {@code foo://bar#/a/b}!

* *

This implementaiton correctly handles this kind of corner cases. For * resolution contexts whose fragment part is not a JSON Pointer, an exact * reference equality is required.

*/ public final class InlineSchemaTree extends JsonSchemaTree { /** * The list of contexts whose URIs bear a JSON Pointer as a fragment part */ private final Map ptrRefs; /** * The list of contexts whose URIs bear a non JSON Pointer fragment part */ private final Map otherRefs; public InlineSchemaTree(final JsonRef loadingRef, final JsonNode baseNode) { super(loadingRef, baseNode, Dereferencing.INLINE); final Map ptrMap = Maps.newHashMap(); final Map otherMap = Maps.newHashMap(); walk(currentRef, currentNode, JsonPointer.empty(), ptrMap, otherMap); ptrRefs = ImmutableMap.copyOf(ptrMap); otherRefs = ImmutableMap.copyOf(otherMap); } public InlineSchemaTree(final JsonNode baseNode) { this(JsonRef.emptyRef(), baseNode); } /** * Tell whether a full resolved reference is contained within this tree * *

Given the pecularity of inline dereferencing, this operation is not * as simple as for canonical trees. The algorithm is as follows:

* *
    *
  • if the fragment part of the target reference is not a JSON * Pointer, only non JSON Pointer resolution contexts are considered, * and return {@code true} if and only if an exact match is found, * {@code false} otherwise;
  • *
  • at this point, we know the fragment part is a JSON Pointer; * compare the reference without a fragment part to all "JSON Pointer * enabled" resolution contexts, without a fragment part: if there is * no match, return {@code false};
  • *
  • on a match, return {@code true} if the JSON Pointer of the * matched context is a parent of the ref's JSON Pointer.
  • *
* * @param ref the target reference * @return see description * @see JsonPointer#isParentOf(JsonPointer) */ @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; } 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.

* * @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(); // Note: we are guaranteed that loadingRef has an empty fragment if (loadingRef.contains(ref)) return refPtr; 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)); } return 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