
net.pwall.json.pointer.JSONReference Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of json-pointer Show documentation
Show all versions of json-pointer Show documentation
Java implementation of JSON Pointer
The newest version!
/*
* @(#) JSONReference.java
*
* json-pointer Java implementation of JSON Pointer
* Copyright (c) 2021 Peter Wall
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.pwall.json.pointer;
import java.util.Objects;
import net.pwall.json.JSONMapping;
import net.pwall.json.JSONSequence;
import net.pwall.json.JSONValue;
/**
* A JSON Reference - a combination of a JSON Pointer and the JSON value to which it refers. This allows for a single
* object to be used (and passed as a parameter between functions) in the common case of a pointer being employed to
* navigate a tree of JSON values.
*
* The class is implemented as a derived class from {@link JSONPointer} - this is an optimisation to reduce object
* allocation.
*
* @author Peter Wall
*/
public class JSONReference extends JSONPointer {
private final JSONValue base;
private final boolean valid;
private final JSONValue value;
/**
* Private constructor (used by {@link #child(int)}/{@link #child(String)} methods).
*
* @param base the base {@link JSONValue}
* @param tokens a list of tokens representing the pointer
* @param valid {@code true} if the value is valid
* @param value the value pointed to by the pointer
*/
private JSONReference(JSONValue base, String[] tokens, boolean valid, JSONValue value) {
super(tokens);
this.base = base;
this.valid = valid;
this.value = value;
}
/**
* Private constructor (used by {@link #parent()} method).
*
* @param base the base {@link JSONValue}
* @param tokens a list of tokens representing the pointer
*/
private JSONReference(JSONValue base, String[] tokens) {
super(tokens);
this.base = base;
if (exists(base)) {
valid = true;
value = eval(base);
}
else {
valid = false;
value = null;
}
}
/**
* Create a JSON Reference using the given base JSON value and pointer.
*
* @param base the base {@link JSONValue}
* @param pointer a {@link JSONPointer} to a node within the base value
*/
public JSONReference(JSONValue base, JSONPointer pointer) {
this(base, checkNotNull(pointer).getTokens());
}
/**
* Create a JSON Reference using the given base JSON value and a pointer in string form.
*
* @param base the base {@link JSONValue}
* @param string a string representing a JSON Pointer
* @throws JSONPointerException if the string is not either an empty string, or starts with "/"
*/
public JSONReference(JSONValue base, String string) {
this(base, parse(checkNotNull(string)));
}
/**
* Create a JSON Reference using the given base JSON value and a root pointer.
*
* @param base the base {@link JSONValue}
*/
public JSONReference(JSONValue base) {
this(base, emptyArray, base != null, base);
}
/**
* Get the pointer component of the reference.
*
* Note that since this class derives from {@link JSONPointer}, then provided the virtual functions (e.g.
* {@code toString()}) are not required, the object may simply be cast to a {@link JSONPointer}.
*
* @return the pointer
*/
@Override
public JSONPointer getPointer() {
return new JSONPointer(getTokens());
}
/**
* Get the base {@link JSONValue}.
*
* @return the {@link JSONValue}
*/
public JSONValue getBase() {
return base;
}
/**
* Test whether the reference is valid, i.e. the pointer points to a valid location within the base.
*
* @return {@code true} iff the reference is valid
*/
public boolean isValid() {
return valid;
}
/**
* Get the value referred to by this reference.
*
* @return the value, or {@code null} if the reference is not valid
*/
public JSONValue getValue() {
return value;
}
/**
* Test whether the reference has the nominated child (by name).
*
* @param name the name of the child
* @return {@code true} iff that child exists
*/
public boolean hasChild(String name) {
checkNotNull(name);
return valid && value instanceof JSONMapping && ((JSONMapping>)value).containsKey(name);
}
/**
* Test whether the reference has the nominated child (by index).
*
* @param index the index of the child
* @return {@code true} iff that child exists
*/
public boolean hasChild(int index) {
return valid && ((value instanceof JSONSequence && index >= 0 && index < ((JSONSequence>)value).size()) ||
(value instanceof JSONMapping && ((JSONMapping>)value).containsKey(Integer.toString(index))));
}
/**
* Navigate to the parent of the currently-addressed element.
*
* @return a reference that refers to the parent element
* @throws JSONPointerException on any attempt to get the parent of the root element
*/
@Override
public JSONReference parent() {
String[] tokens = getTokens();
int n = tokens.length;
if (n == 0)
throw new JSONPointerException("Can't get parent of root JSON Pointer");
String[] newTokens = new String[--n];
System.arraycopy(tokens, 0, newTokens, 0, n);
return new JSONReference(base, newTokens);
}
/**
* Navigate to the named child of the currently-addressed element.
*
* @param name the name of the child
* @return a reference that refers to the child element (possibly not valid)
*/
@Override
public JSONReference child(String name) {
checkNotNull(name);
String[] tokens = getTokens();
int n = tokens.length;
String[] newTokens = new String[n + 1];
System.arraycopy(tokens, 0, newTokens, 0, n);
newTokens[n] = name;
if (valid && value instanceof JSONMapping) {
JSONMapping> mapping = (JSONMapping>)value;
if (mapping.containsKey(name))
return new JSONReference(base, newTokens, true, mapping.get(name));
}
return new JSONReference(base, newTokens, false, null);
}
/**
* Navigate to the numbered child of the currently-addressed element (must be an array).
*
* @param index the index of the child
* @return a reference that refers to the child element (possibly not valid)
* @throws JSONPointerException if the index is negative
*/
@Override
public JSONReference child(int index) {
if (index < 0)
throw new JSONPointerException("JSON Pointer index must not be negative");
String[] tokens = getTokens();
int n = tokens.length;
String[] newTokens = new String[n + 1];
System.arraycopy(tokens, 0, newTokens, 0, n);
newTokens[n] = Integer.toString(index);
if (valid) {
if (value instanceof JSONSequence) {
JSONSequence> sequence = (JSONSequence>)value;
if (index < sequence.size())
return new JSONReference(base, newTokens, true, sequence.get(index));
}
else if (value instanceof JSONMapping) {
String name = Integer.toString(index);
JSONMapping> mapping = (JSONMapping>)value;
if (mapping.containsKey(name))
return new JSONReference(base, newTokens, true, mapping.get(name));
}
}
return new JSONReference(base, newTokens, false, null);
}
/**
* Attempt to locate the specified child {@link JSONValue} within a nested structure. The function will return a
* {@link JSONReference} to the target, or {@code null} if the target can not be located.
*
* Note that this will perform a depth-first search of the entire structure, comparing on object identity, not
* equality.
*
* @param target the target of the search
* @return a {@code JSONReference} referring to the base value and the target
*/
public JSONReference locateChild(JSONValue target) {
if (value == target)
return this;
if (value instanceof JSONMapping) {
JSONMapping> mapping = (JSONMapping>)value;
for (String key : mapping.keySet()) {
JSONReference nested = child(key).locateChild(target);
if (nested != null)
return nested;
}
}
else if (value instanceof JSONSequence) {
JSONSequence> sequence = (JSONSequence>)value;
for (int i = 0, n = sequence.size(); i < n; i++) {
JSONReference nested = child(i).locateChild(target);
if (nested != null)
return nested;
}
}
return null;
}
/**
* Compare two JSON references for equality. The references are equal only if the pointers are equal and the base
* values refer to the same object.
*
* @param other the other object for comparison
* @return {@code true} iff the objects are equal
*/
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof JSONReference) || !super.equals(other))
return false;
JSONReference otherRef = (JSONReference)other;
return base == otherRef.base && valid == otherRef.valid && value == otherRef.value;
}
/**
* Get the hash code for the reference.
*
* @return the hash code
*/
@Override
public int hashCode() {
return super.hashCode() ^ Objects.hashCode(base) ^ (valid ? 1 : 0) ^ Objects.hashCode(value);
}
/**
* Get a string representation of the reference.
*
* @return a string representation of the value (or "invalid" if the reference is not valid)
*/
@Override
public String toString() {
return valid ? value == null ? "null" : value.toJSON() : "invalid";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy