
io.progix.dropwizard.patch.JsonPath Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dropwizard-patch Show documentation
Show all versions of dropwizard-patch Show documentation
An implementation of the HTTP method PATCH using RFC6902 for the Dropwizard framework.
The newest version!
/*
* Copyright 2014 Tariq Bugrara
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.progix.dropwizard.patch;
import com.fasterxml.jackson.core.JsonPointer;
import io.progix.dropwizard.patch.exception.InvalidPatchPathException;
import java.util.ArrayList;
import java.util.List;
/**
* Wrapper class for Jackson's {@link JsonPointer}
*
* Created to be more user-friendly for explicit patching. This class is used to determine what a String Json path as
* defined in RFC6901 points to
*/
public class JsonPath {
private List properties;
private List elements;
private String pathString;
private JsonPointer jsonPointer;
private int size;
/**
* Creates the path using a {@link JsonPointer} by iterating through the segments and creating {@link
* JsonPathProperty} and {@link JsonPathElement} for each segment.
*
* If a given segment does not match as a String property, an empty {@link JsonPathProperty} is created.
*
* If a given segment does not match as a Integer index, an empty {@link JsonPathElement} is created.
*
* @param pointer
*
* @see com.fasterxml.jackson.core.JsonPointer
*/
public JsonPath(JsonPointer pointer) {
this.jsonPointer = pointer;
this.properties = new ArrayList<>();
this.elements = new ArrayList<>();
this.pathString = "";
while (pointer != null) {
if (pointer.getMatchingProperty() != null && !pointer.getMatchingProperty().isEmpty()) {
size++;
}
// Keep in mind, Jackson's implementation of JsonPointer allows all segments to be properties
if (pointer.mayMatchProperty() && !pointer.mayMatchElement() && !pointer.getMatchingProperty()
.isEmpty() && !pointer.getMatchingProperty().equals("-")) {
properties.add(new JsonPathProperty(pointer.getMatchingProperty()));
this.pathString += "/" + pointer.getMatchingProperty();
} else if (pointer.getMatchingProperty().equals("-")) {
// This character represents the last element in an array
elements.add(new JsonPathElement(true));
this.pathString += pointer.getMatchingProperty() + "/";
} else {
properties.add(new JsonPathProperty());
}
if (pointer.mayMatchElement()) {
elements.add(new JsonPathElement(pointer.getMatchingIndex()));
this.pathString += "/" + pointer.getMatchingIndex();
} else {
elements.add(new JsonPathElement());
}
pointer = pointer.tail();
}
}
/**
* Convenience method to retrieve all {@link JsonPathProperty} for this path
*
* @return the list of {@link JsonPathProperty}
*/
public List getProperties() {
return properties;
}
/**
* Convenience method to retrieve all {@link JsonPathElement} for this path
*
* @return the list of {@link JsonPathElement}
*/
public List getElements() {
return elements;
}
/**
* @return the number of segments in this path
*/
public int size() {
return size;
}
/**
* Determines if this JsonPath is equivalent to the given String path.
*
* Note that there should be no leading slash in the given path.
*
* This method treats the string "#" as a special meaning to signify a numerical segment within the JsonPointer. If
* you use the '#' character as a property name, use "~#" to properly treat that segment as a property and not an
* array element.
*
* @param path String path to check
*
* @return true if the path is given is equivalent to this JsonPath, false otherwise.
*/
public boolean is(String path) {
if (path.charAt(0) != '/') {
throw new IllegalArgumentException("Paths must start with a '/'");
}
// Remove first slash which is guaranteed
path = path.substring(1, path.length());
String[] tokens = path.split("/");
if (!endsAt(tokens.length - 1)) {
return false;
}
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (property(i).exists()) {
// Can't be true because a property exists at i and path is checking for an element
if (token.equals("#")) {
return false;
}
// Convert special character to "#"
if (token.equals("~#")) {
token = "#";
}
if (!token.equals(property(i).val())) {
return false;
}
} else if (element(i).exists()) {
if (token.equals("#") || (token.equals("-") && element(i).isEndOfArray()) || String
.valueOf(element(i).val()).equals(token)) {
return true;
}
} else {
return false;
}
}
return true;
}
/**
* This method can be used to determine when an {@link InvalidPatchPathException} should be thrown. Uses this
* exception and method provides useful information for the client when trying to patch a resource in a way the
* server does not support
*
* Ex. If the path is "/a/b/c", endsAt(2) will return true while endsAt(1) and endsAt(3) will return false
*
* @param index The index to test if this path ends
*
* @return true if the index provided is the last segment to contain data, false otherwise
*/
public boolean endsAt(int index) {
int next = index + 1;
return (property(index).exists() || element(index).exists()) && !property(next).exists() && !element(next)
.exists();
}
/**
* This method will never return null. If trying to access a {@link JsonPathProperty} for a segment that is not a
* String property, will return a special object who's {@link JsonPathProperty#exists()} will return false
*
* @param index the segment index to retrieve
*
* @return a {@link JsonPathProperty} for this index
*/
public JsonPathProperty property(int index) {
if (index >= properties.size()) {
return new JsonPathProperty();
}
return properties.get(index);
}
/**
* This method will never return null. If trying to access a {@link JsonPathElement} for a segment that is not an
* Integer property, will return a special object who's {@link JsonPathElement#exists()} will return false
*
* @param index the segment index to retrieve
*
* @return a {@link JsonPathElement} for this index
*/
public JsonPathElement element(int index) {
if (index >= elements.size()) {
return new JsonPathElement();
}
return elements.get(index);
}
@Override
public String toString() {
return pathString;
}
public JsonPointer getJsonPointer() {
return jsonPointer;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy