
com.github.fge.jsonschema.ref.JsonRef 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.ref;
import com.github.fge.jsonschema.exceptions.JsonReferenceException;
import com.github.fge.jsonschema.jsonpointer.JsonPointer;
import com.github.fge.jsonschema.jsonpointer.JsonPointerException;
import com.github.fge.jsonschema.report.ProcessingMessage;
import com.google.common.base.Preconditions;
import java.net.URI;
import java.net.URISyntaxException;
import static com.github.fge.jsonschema.messages.RefProcessingMessages.*;
/**
* Representation of a JSON Reference
*
* JSON
* Reference, currently a draft, is a way to define a path within a JSON
* document.
*
* To quote the draft, "A JSON Reference is a JSON object, which contains
* a member named "$ref", which has a JSON string value." This string value
* must be a URI. Example:
*
*
* {
* "$ref": "http://example.com/example.json#/foo/bar"
* }
*
*
* This class differs from the JSON Reference draft in one important way:
* normally, the fragment part of a JSON Reference must be a JSON Pointer;
* however, this means you cannot use a reference for addressing inner schemas
* when inline addressing mode is used. We therefore choose to accept all
* possible fragment parts.
*
* The implementation is a wrapper over Java's {@link URI}, with the
* following differences:
*
*
* - all URIs are normalized from the get go;
* - an empty fragment is equivalent to no fragment at all, and stands for
* a root JSON Pointer;
* - a reference is taken to be absolute if the underlying URI is absolute
* and it has no fragment, or an empty fragment.
*
*
* It also special cases the following:
*
*
* - an empty reference (for instance, used in anonymouns schemas);
* - URIs with the {@code jar} scheme (the resolving algorithm differs --
* please note that this breaks URI resolution rules).
*
*
* This class is thread safe and immutable.
*/
public abstract class JsonRef
{
private static final URI EMPTY_URI = URI.create("");
protected static final URI HASHONLY_URI = URI.create("#");
/**
* Whether this JSON Reference is legal
*
* It is legal if and only if there is no fragment, or the fragment part
* is a JSON Pointer.
*/
protected final boolean legal;
/**
* The URI, as provided by the input, with an appended empty fragment if
* no fragment was provided
*/
protected final URI uri;
/**
* The locator of this reference. This is the URI with an empty fragment
* part.
*/
protected final URI locator;
/**
* The pointer of this reference, if any
*
* Initialized to null if the fragment part is not a JSON Pointer.
*/
protected final JsonPointer pointer;
/**
* String representation
*/
private final String asString;
/**
* Hashcode
*/
private final int hashCode;
/**
* Main constructor, {@code protected} by design
*
* @param uri the URI to build that reference
*/
protected JsonRef(final URI uri)
{
final String scheme = uri.getScheme();
final String ssp = uri.getSchemeSpecificPart();
final String uriFragment = uri.getFragment();
final String realFragment = uriFragment == null ? "" : uriFragment;
boolean isLegal = true;
JsonPointer ptr;
try {
this.uri = new URI(scheme, ssp, realFragment);
locator = new URI(scheme, ssp, "");
try {
ptr = realFragment.isEmpty() ? JsonPointer.empty()
: new JsonPointer(realFragment);
} catch (JsonPointerException ignored) {
ptr = null;
isLegal = false;
}
legal = isLegal;
pointer = ptr;
asString = this.uri.toString();
hashCode = asString.hashCode();
} catch (URISyntaxException e) {
throw new RuntimeException("WTF??", e);
}
}
/**
* Build a JSON Reference from a URI
*
* @param uri the provided URI
* @return the JSON Reference
* @throws NullPointerException the provided URI is null
*/
public static JsonRef fromURI(final URI uri)
{
Preconditions.checkNotNull(uri, "URI must not be null");
final URI normalized = uri.normalize();
if (HASHONLY_URI.equals(normalized) || EMPTY_URI.equals(normalized))
return EmptyJsonRef.getInstance();
return "jar".equals(normalized.getScheme())
? new JarJsonRef(normalized)
: new HierarchicalJsonRef(normalized);
}
/**
* Build a JSON Reference from a string input
*
* @param s the string
* @return the reference
* @throws JsonReferenceException string is not a valid URI
* @throws NullPointerException provided string is null
*/
public static JsonRef fromString(final String s)
throws JsonReferenceException
{
Preconditions.checkNotNull(s, "string must not be null");
try {
return fromURI(new URI(s));
} catch (URISyntaxException e) {
final ProcessingMessage message = new ProcessingMessage()
.message(INVALID_URI).put("uri", s);
throw new JsonReferenceException(message, e);
}
}
/**
* Return an empty reference
*
* An empty reference is a reference which only has an empty fragment.
*
*
* @return see above
*/
public static JsonRef emptyRef()
{
return EmptyJsonRef.getInstance();
}
/**
* Return the underlying URI for this JSON Reference
*
* @return the URI
*/
public final URI toURI()
{
return uri;
}
/**
* Tell whether this reference is an absolute reference
*
* A JSON Reference is considered absolute iif the underlying URI is
* itself absolute and it has an empty, or no, fragment part.
*
* @return see above
*/
public abstract boolean isAbsolute();
/**
* Resolve this reference against another reference
*
* @param other the reference to resolve
* @return the resolved reference
*/
public abstract JsonRef resolve(final JsonRef other);
/**
* Return this JSON Reference's locator
*
* This returns the reference with an empty fragment.
*
* @return an URI
*/
public final URI getLocator()
{
return locator;
}
public final boolean isLegal()
{
return legal;
}
public final JsonPointer getPointer()
{
return pointer;
}
/**
* Tell whether the current JSON Reference "contains" another
*
* This is considered true iif both references have the same locator,
* in other words, if they differ only by their fragment part.
*
* @param other the other reference
* @return see above
*/
public final boolean contains(final JsonRef other)
{
return locator.equals(other.locator);
}
@Override
public final int hashCode()
{
return hashCode;
}
@Override
public final boolean equals(final Object obj)
{
if (obj == null)
return false;
if (this == obj)
return true;
if (!(obj instanceof JsonRef))
return false;
final JsonRef that = (JsonRef) obj;
return asString.equals(that.asString);
}
@Override
public final String toString()
{
return asString;
}
}