org.eel.kitchen.jsonschema.ref.JsonRef Maven / Gradle / Ivy
Show all versions of json-schema-validator Show documentation
/*
* Copyright (c) 2012, 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 org.eel.kitchen.jsonschema.ref;
import com.google.common.base.Preconditions;
import org.eel.kitchen.jsonschema.main.JsonSchemaException;
import org.eel.kitchen.jsonschema.report.Domain;
import org.eel.kitchen.jsonschema.report.Message;
import org.eel.kitchen.jsonschema.schema.AddressingMode;
import org.eel.kitchen.jsonschema.schema.InlineSchemaContainer;
import java.net.URI;
import java.net.URISyntaxException;
/**
* 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.
*
* @see AddressingMode#INLINE
* @see InlineSchemaContainer
*/
public abstract class JsonRef
{
private static final URI EMPTY_URI = URI.create("");
protected static final URI HASHONLY_URI = URI.create("#");
/**
* 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 fragment of this reference.
*
* @see JsonFragment
*/
protected final JsonFragment fragment;
/**
* 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;
try {
this.uri = new URI(scheme, ssp, realFragment);
locator = new URI(scheme, ssp, "");
fragment = JsonFragment.fromFragment(realFragment);
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 JsonSchemaException string is not a valid URI
* @throws NullPointerException provided string is null
*/
public static JsonRef fromString(final String s)
throws JsonSchemaException
{
Preconditions.checkNotNull(s, "string must not be null");
try {
return fromURI(new URI(s));
} catch (URISyntaxException e) {
final Message.Builder msg = Domain.REF_RESOLVING.newMessage()
.setKeyword("N/A").addInfo("uri", s).setMessage("invalid URI");
throw new JsonSchemaException(msg.build(), 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;
}
/**
* Return this JSON Reference's fragment
*
* @return the fragment
*/
public final JsonFragment getFragment()
{
return fragment;
}
/**
* 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;
}
}