com.zuunr.jsonpointer.JsonPointer Maven / Gradle / Ivy
/*
* Copyright 2020 Zuunr AB
*
* 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 com.zuunr.jsonpointer;
import com.zuunr.json.JsonArray;
import com.zuunr.json.JsonArrayBuilder;
import com.zuunr.json.JsonValue;
import com.zuunr.json.JsonValueSupport;
/**
* @author Niklas Eldberger
*/
public class JsonPointer {
private static final JsonValue FRAGMENT_START = JsonValue.of("#");
private static final JsonPointer JSON_DOCUMENT_ROOT = new JsonPointer("#",JsonArray.EMPTY);
private String asString;
private JsonArray asArray;
public static JsonPointer of(String pointer) {
JsonArrayBuilder builder = JsonArray.EMPTY.builder();
JsonArray array = JsonArray.of(pointer.split("/"));
if (pointer.endsWith("/")) {
array = array.add("");
}
if (FRAGMENT_START.equals(array.head())) {
array = array.tail();
}
for (JsonValue referenceTokenJsonValue : array) {
Integer integer = integerOf(referenceTokenJsonValue.getString());
if (integer == null) {
JsonValue unescaped = unescape(referenceTokenJsonValue);
builder.add(unescaped);
} else {
builder.add(integer);
}
}
return new JsonPointer(pointer, builder.build());
}
public static JsonPointer of(JsonArray array) {
if (array.isEmpty()) {
return JSON_DOCUMENT_ROOT;
}
JsonArrayBuilder builder = JsonArray.EMPTY.builder();
StringBuilder stringBuilder = null;
for (JsonValue unescapedReferenceToken : array) {
JsonValue escaped;
if (unescapedReferenceToken.isString()) {
escaped = escape(unescapedReferenceToken);
} else if (unescapedReferenceToken.isInteger()) {
escaped = unescapedReferenceToken;
} else {
throw new RuntimeException("Only Integer and String is supported");
}
if (stringBuilder == null) {
stringBuilder = new StringBuilder();
} else {
stringBuilder.append("/");
}
builder.add(escaped);
stringBuilder.append(escaped.isString() ? escaped.getString() : escaped.toString());
}
if (stringBuilder == null) {
// FIXME Should stringBuilder be nullable?
throw new IllegalStateException("Array was empty");
}
return new JsonPointer(stringBuilder.toString(), builder.build());
}
private JsonPointer(String pointer, JsonArray pointerArray) {
this.asString = pointer;
this.asArray = pointerArray;
}
private static JsonValue unescape(JsonValue referenceTokenJsonValue) {
String unescaped = referenceTokenJsonValue.getString().replace("~1", "/");
unescaped = unescaped.replace("~0", "~");
unescaped = decodeHex(unescaped);
return JsonValue.of(unescaped);
}
public static String decodeHex(String stringWithHex) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < stringWithHex.length(); i++) {
char charToAppend;
if (stringWithHex.charAt(i) == '%' && i < stringWithHex.length() - 2) {
String hexValue = stringWithHex.substring(i + 1, i + 3);
charToAppend = (char) Integer.parseInt(hexValue, 16);
i = i + 2;
} else {
charToAppend = stringWithHex.charAt(i);
}
builder.append(charToAppend);
}
return builder.toString();
}
private static JsonValue escape(JsonValue unescaped) {
String escaped = unescaped.getString().replace("~", "~0");
escaped = escaped.replace("/", "~1");
return JsonValue.of(escaped);
}
public JsonArray asJsonArray() {
return asArray;
}
public String asString() {
return asString;
}
@Override
public String toString() {
return asString();
}
private static Integer integerOf(String string) {
try {
return Integer.parseInt(string);
} catch (Exception e) {
return null;
}
}
}