diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 87d17607..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-language: java
-jdk:
- - openjdk8
- - openjdk11
-install: {}
-script:
- - ./gradlew assemble check
-
-before_cache:
- - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
-
-cache:
- directories:
- - $HOME/.gradle/caches/
- - $HOME/.gradle/wrapper/
-
diff --git a/project.gradle b/project.gradle
index e306fce2..2776303a 100644
--- a/project.gradle
+++ b/project.gradle
@@ -20,7 +20,7 @@
/*
* Project-specific settings. Unfortunately we cannot put the name in there!
*/
-group = "com.github.java-json-tools";
+group = "com.gravity9.java-json-tools";
sourceCompatibility = JavaVersion.VERSION_1_7;
targetCompatibility = JavaVersion.VERSION_1_7; // defaults to sourceCompatibility
project.ext.description = "JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7386) implementation in Java, using extended TMF620 JsonPath syntax";
diff --git a/src/main/java/com/github/fge/jsonpatch/AddOperation.java b/src/main/java/com/github/fge/jsonpatch/AddOperation.java
deleted file mode 100644
index 4fc3613f..00000000
--- a/src/main/java/com/github/fge/jsonpatch/AddOperation.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.jayway.jsonpath.DocumentContext;
-import com.jayway.jsonpath.JsonPath;
-
-
-/**
- * JSON Patch {@code add} operation
- *
- *
For this operation, {@code path} is the JSON Pointer where the value
- * should be added, and {@code value} is the value to add.
- *
- *
Note that if the target value pointed to by {@code path} already exists,
- * it is replaced. In this case, {@code add} is equivalent to {@code replace}.
- *
- *
- *
Note also that a value will be created at the target path if and only
- * if the immediate parent of that value exists (and is of the correct
- * type).
- *
- *
Finally, if the last reference token of the JSON Pointer is {@code -} and
- * the immediate parent is an array, the given value is added at the end of the
- * array. For instance, applying:
- *
- *
- * { "op": "add", "path": "/-", "value": 3 }
- *
- *
- *
to:
- *
- *
- * [ 1, 2 ]
- *
- *
- *
will give:
- *
- *
- * [ 1, 2, 3 ]
- *
- */
-public final class AddOperation extends PathValueOperation {
- public static final String LAST_ARRAY_ELEMENT_SYMBOL = "-";
-
- @JsonCreator
- public AddOperation(@JsonProperty("path") final String path,
- @JsonProperty("value") final JsonNode value) {
- super("add", path, value);
- }
-
- @Override
- public JsonNode applyInternal(final JsonNode node) throws JsonPatchException {
- if (path.isEmpty()) {
- return value;
- }
- PathDetails pathDetails = PathParser.getParentPathAndNewNodeName(path);
- final String pathToParent = pathDetails.getPathToParent();
- final String newNodeName = pathDetails.getNewNodeName();
-
- final DocumentContext nodeContext = JsonPath.parse(node.deepCopy());
- final JsonNode evaluatedJsonParents = nodeContext.read(pathToParent);
- if (evaluatedJsonParents == null) {
- throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchParent"));
- }
- if (!evaluatedJsonParents.isContainerNode()) {
- throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer"));
- }
-
- if (pathDetails.doesContainFiltersOrMultiIndexesNotation()) { // json filter result is always a list
- for (int i = 0; i < evaluatedJsonParents.size(); i++) {
- JsonNode parentNode = evaluatedJsonParents.get(i);
- if (!parentNode.isContainerNode()) {
- throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer"));
- }
- DocumentContext containerContext = JsonPath.parse(parentNode);
- if (parentNode.isArray()) {
- addToArray(containerContext, "$", newNodeName);
- } else {
- addToObject(containerContext, "$", newNodeName);
- }
- }
- return nodeContext.read("$");
- } else {
- return evaluatedJsonParents.isArray()
- ? addToArray(nodeContext, pathToParent, newNodeName)
- : addToObject(nodeContext, pathToParent, newNodeName);
- }
- }
-
- private JsonNode addToArray(final DocumentContext node, String jsonPath, String newNodeName) throws JsonPatchException {
- if (newNodeName.equals(LAST_ARRAY_ELEMENT_SYMBOL)) {
- return node.add(jsonPath, value).read("$", JsonNode.class);
- }
-
- final int size = node.read(jsonPath, JsonNode.class).size();
- final int index = verifyAndGetArrayIndex(newNodeName, size);
-
- ArrayNode updatedArray = node.read(jsonPath, ArrayNode.class).insert(index, value);
- return "$".equals(jsonPath) ? updatedArray : node.set(jsonPath, updatedArray).read("$", JsonNode.class);
- }
-
- private JsonNode addToObject(final DocumentContext node, String jsonPath, String newNodeName) {
- return node
- .put(jsonPath, newNodeName, value)
- .read("$", JsonNode.class);
- }
-
- private int verifyAndGetArrayIndex(String stringIndex, int size) throws JsonPatchException {
- int index;
- try {
- index = Integer.parseInt(stringIndex);
- } catch (NumberFormatException ignored) {
- throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.notAnIndex"));
- }
- if (index < 0 || index > size) {
- throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchIndex"));
- }
- return index;
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java b/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java
deleted file mode 100644
index 41d4a49d..00000000
--- a/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
-
-import java.io.IOException;
-
-/**
- * Base class for JSON Patch operations taking two JSON Pointers as arguments
- */
-public abstract class DualPathOperation extends JsonPatchOperation {
- @JsonSerialize(using = ToStringSerializer.class)
- protected final String from;
-
- /**
- * Protected constructor
- *
- * @param op operation name
- * @param from source path
- * @param path destination path
- */
- protected DualPathOperation(final String op, final String from, final String path) {
- super(op, path);
- this.from = from;
- }
-
- @Override
- public final void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonProcessingException {
- jgen.writeStartObject();
- jgen.writeStringField("op", op);
- jgen.writeStringField("path", path);
- jgen.writeStringField("from", from);
- jgen.writeEndObject();
- }
-
- @Override
- public final void serializeWithType(final JsonGenerator jgen, final SerializerProvider provider,
- final TypeSerializer typeSer) throws IOException, JsonProcessingException {
- serialize(jgen, provider);
- }
-
- public final String getFrom() {
- return from;
- }
-
- @Override
- public final String toString() {
- return "op: " + op + "; from: \"" + from + "\"; path: \"" + path + '"';
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/Iterables.java b/src/main/java/com/github/fge/jsonpatch/Iterables.java
deleted file mode 100644
index 79b67e1b..00000000
--- a/src/main/java/com/github/fge/jsonpatch/Iterables.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.github.fge.jsonpatch;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-/**
- * Iterables utility class
- * @author {@literal @}soberich on 30-Nov-18
- */
-public final class Iterables {
-
- private Iterables() {}
-
- /**
- * Returns the last element of {@code iterable}.
- *
- * @param underlying type being iterated
- * @param iterable type of iterable
- * @return the last element of {@code iterable}
- * @throws NoSuchElementException if the iterable is empty
- */
- public static T getLast(Iterable iterable) {
- Iterator iterator = iterable.iterator();
- while (true) {
- T current = iterator.next();
- if (!iterator.hasNext()) {
- return current;
- }
- }
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java
deleted file mode 100644
index 23d6819d..00000000
--- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.github.fge.jsonpatch;
-
-public class JsonPathParser {
-
- private static final String ARRAY_ELEMENT_REGEX = "(?<=\\.)(\\d+)";
-
- public static String tmfStringToJsonPath(String path) throws JsonPatchException {
- if (path.startsWith("$")) {
- return path;
- } else if (path.contains("?")) {
- throw new JsonPatchException("Invalid path, `?` are not allowed in JsonPointer expressions.");
- } else if (path.contains("//")) {
- throw new JsonPatchException("Invalid path, `//` is not allowed in JsonPointer expressions.");
- }
-
- return "$" + path.replace('/', '.')
- .replace("~1", "/") // / must be escaped in JsonPointer using ~1
- .replace("~0", "~") // ~ must be escaped in JsonPointer using ~0
- .replaceAll(ARRAY_ELEMENT_REGEX, "[$1]");
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/Patch.java b/src/main/java/com/github/fge/jsonpatch/Patch.java
deleted file mode 100644
index adce2042..00000000
--- a/src/main/java/com/github/fge/jsonpatch/Patch.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.github.fge.jsonpatch;
-
-import com.fasterxml.jackson.databind.JsonNode;
-
-public interface Patch {
-
- JsonNode apply(JsonNode node) throws JsonPatchException;
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/PathDetails.java b/src/main/java/com/github/fge/jsonpatch/PathDetails.java
deleted file mode 100644
index d8ecf7fb..00000000
--- a/src/main/java/com/github/fge/jsonpatch/PathDetails.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.github.fge.jsonpatch;
-
-public class PathDetails {
-
- private final String pathToParent;
-
- private final String newNodeName;
-
- private final boolean containsFiltersOrMultiIndexesNotation;
-
- public PathDetails(String pathToParent, String newNodeName, boolean containsFiltersOrMultiIndexesNotation) {
- this.pathToParent = pathToParent;
- this.newNodeName = newNodeName;
- this.containsFiltersOrMultiIndexesNotation = containsFiltersOrMultiIndexesNotation;
- }
-
- public String getPathToParent() {
- return pathToParent;
- }
-
- public String getNewNodeName() {
- return newNodeName;
- }
-
- public boolean doesContainFiltersOrMultiIndexesNotation() {
- return containsFiltersOrMultiIndexesNotation;
- }
-
- @Override
- public String toString() {
- return "PathDetails{" +
- "pathToParent='" + pathToParent + '\'' +
- ", newNodeName='" + newNodeName + '\'' +
- ", containsFiltersOrMultiIndexesNotation=" + containsFiltersOrMultiIndexesNotation +
- '}';
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/PathParser.java b/src/main/java/com/github/fge/jsonpatch/PathParser.java
deleted file mode 100644
index 0ae4aa37..00000000
--- a/src/main/java/com/github/fge/jsonpatch/PathParser.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package com.github.fge.jsonpatch;
-
-import com.jayway.jsonpath.internal.Path;
-import com.jayway.jsonpath.internal.path.PathCompiler;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PathParser {
-
- private static final String FILTER_PLACEHOLDER = "[?]";
-
- /**
- * This method parses JsonPath to find node name that needs to be added and path to the parent of new node.
- * Additionally, it finds if path contains filter or multi index notation (like [1:5])
- *
- * @param path Path in JsonPath or JsonPointer notation
- * @return PathDetails containing path to parent, name of new node and boolean value if path contains filter or multi
- * index notation
- * @throws JsonPatchException when invalid path provided
- * */
- public static PathDetails getParentPathAndNewNodeName(String path) throws JsonPatchException {
- final String fullJsonPath = JsonPathParser.tmfStringToJsonPath(path);
- final Path compiledPath = compilePath(fullJsonPath);
- String[] splitJsonPath = splitJsonPath(compiledPath);
-
- int filterCounter = 0;
- List filters = getFiltersOperations(fullJsonPath);
- boolean containsFiltersOrMultiIndexNotation = false;
- StringBuilder sb = new StringBuilder();
- sb.append("$");
- for (int i = 0; i < splitJsonPath.length - 1; i++) {
- if (splitJsonPath[i].isEmpty()) {
- continue;
- }
- if (splitJsonPath[i].equals(FILTER_PLACEHOLDER)) {
- sb.append(filters.get(filterCounter++));
- containsFiltersOrMultiIndexNotation = true;
- } else if (isArrayPart(splitJsonPath[i])) {
- sb.append(splitJsonPath[i]);
- if (isMultiIndexNotation(splitJsonPath[i])) {
- containsFiltersOrMultiIndexNotation = true;
- }
- } else if (isDoubleDot(splitJsonPath[i])) {
- sb.append(splitJsonPath[i]);
- } else {
- sb.append("[").append(splitJsonPath[i]).append("]");
- }
- }
- final String pathToParent = sb.toString();
- final String newNodeName = getNewNodeName(splitJsonPath);
- return new PathDetails(pathToParent, newNodeName, containsFiltersOrMultiIndexNotation);
- }
-
- private static boolean isMultiIndexNotation(String path) {
- String pathWithoutBracket = path
- .replace("[", "")
- .replace("]", "");
- return !pathWithoutBracket.startsWith("'") && !pathWithoutBracket.matches("[0-9]+");
- }
-
- private static String getNewNodeName(String[] splitJsonPath) {
- return splitJsonPath[splitJsonPath.length - 1]
- .replace("'", "")
- .replace("[", "")
- .replace("]", "");
- }
-
- /**
- * Removes $ sign from the beginning of the path for easier processing - it will be added later on
- * This method is called after PathCompiler.compile, so we are sure now, that the path is correct
- * and bracket notation is used.
- * We can now split JsonPath using positive lookahead regex (without removing separator).
- * */
- private static String[] splitJsonPath(Path compiledPath) {
- return compiledPath.toString()
- .replace("$", "")
- .split("(?=\\[)");
- }
-
- private static Path compilePath(String fullJsonPath) throws JsonPatchException {
- try {
- return PathCompiler.compile(fullJsonPath);
- } catch (Exception e) {
- throw new JsonPatchException("Non-compilable path provided");
- }
- }
-
- private static boolean isDoubleDot(String jsonPathPart) {
- return jsonPathPart.equals("..");
- }
-
- private static boolean isArrayPart(String jsonPathPart) {
- return jsonPathPart.startsWith("[") && jsonPathPart.endsWith("]");
- }
-
- private static List getFiltersOperations(String jsonPath) {
- if (!jsonPath.contains("[?(")) {
- return new ArrayList<>();
- }
- List filters = new ArrayList<>();
- int openingBracketPosition = -1;
- int counter = 0;
- for (int i = 0; i < jsonPath.length(); i++) {
- if (jsonPath.charAt(i) == '[' && jsonPath.charAt(i+1) == '?') {
- if (openingBracketPosition == -1) {
- openingBracketPosition = i;
- }
- counter++;
- }
- if (jsonPath.charAt(i) == ']' && counter > 0) {
- counter--;
- if (counter == 0) {
- filters.add(jsonPath.substring(openingBracketPosition, i+1));
- openingBracketPosition = -1;
- }
- }
- }
- return filters;
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java b/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java
deleted file mode 100644
index e3dab45b..00000000
--- a/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-
-import java.io.IOException;
-
-/**
- * Base class for patch operations taking a value in addition to a path
- */
-public abstract class PathValueOperation
- extends JsonPatchOperation {
- @JsonSerialize
- protected final JsonNode value;
-
- /**
- * Protected constructor
- *
- * @param op operation name
- * @param path affected path
- * @param value JSON value
- */
- protected PathValueOperation(final String op, final String path,
- final JsonNode value) {
- super(op, path);
- this.value = value.deepCopy();
- }
-
- @Override
- public final void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonProcessingException {
- jgen.writeStartObject();
- jgen.writeStringField("op", op);
- jgen.writeStringField("path", path.toString());
- jgen.writeFieldName("value");
- jgen.writeTree(value);
- jgen.writeEndObject();
- }
-
- @Override
- public final void serializeWithType(final JsonGenerator jgen, final SerializerProvider provider,
- final TypeSerializer typeSer) throws IOException, JsonProcessingException {
- serialize(jgen, provider);
- }
-
- public final JsonNode getValue() {
- return value.deepCopy();
- }
-
- @Override
- public final String toString() {
- return "op: " + op + "; path: \"" + path + "\"; value: " + value;
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/diff/DiffOperation.java b/src/main/java/com/github/fge/jsonpatch/diff/DiffOperation.java
deleted file mode 100644
index 9cbe5505..00000000
--- a/src/main/java/com/github/fge/jsonpatch/diff/DiffOperation.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch.diff;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.github.fge.jackson.jsonpointer.JsonPointer;
-import com.github.fge.jsonpatch.AddOperation;
-import com.github.fge.jsonpatch.CopyOperation;
-import com.github.fge.jsonpatch.JsonPatchOperation;
-import com.github.fge.jsonpatch.MoveOperation;
-import com.github.fge.jsonpatch.RemoveOperation;
-import com.github.fge.jsonpatch.ReplaceOperation;
-
-final class DiffOperation
-{
- private final Type type;
- /* An op's "from", if any */
- private final JsonPointer from;
- /* Value displaced by this operation, if any */
- private final JsonNode oldValue;
- /* An op's "path", if any */
- private final JsonPointer path;
- /* An op's "value", if any */
- private final JsonNode value;
-
- static DiffOperation add(final JsonPointer path,
- final JsonNode value)
- {
- return new DiffOperation(Type.ADD, null, null, path, value);
- }
-
- static DiffOperation copy(final JsonPointer from,
- final JsonPointer path, final JsonNode value)
- {
- return new DiffOperation(Type.COPY, from, null, path,
- value);
- }
-
- static DiffOperation move(final JsonPointer from,
- final JsonNode oldValue, final JsonPointer path,
- final JsonNode value)
- {
- return new DiffOperation(Type.MOVE, from, oldValue, path,
- value);
- }
-
- static DiffOperation remove(final JsonPointer from,
- final JsonNode oldValue)
- {
- return new DiffOperation(Type.REMOVE, from, oldValue, null, null);
- }
-
- static DiffOperation replace(final JsonPointer from,
- final JsonNode oldValue, final JsonNode value)
- {
- return new DiffOperation(Type.REPLACE, from, oldValue, null,
- value);
- }
-
- private DiffOperation(final Type type, final JsonPointer from,
- final JsonNode oldValue, final JsonPointer path,
- final JsonNode value)
- {
- this.type = type;
- this.from = from;
- this.oldValue = oldValue;
- this.path = path;
- this.value = value;
- }
-
- Type getType()
- {
- return type;
- }
-
- JsonPointer getFrom()
- {
- return from;
- }
-
- JsonNode getOldValue()
- {
- return oldValue;
- }
-
- JsonPointer getPath()
- {
- return path;
- }
-
- JsonNode getValue()
- {
- return value;
- }
-
- JsonPatchOperation asJsonPatchOperation()
- {
- return type.toOperation(this);
- }
-
- enum Type {
- ADD
- {
- @Override
- JsonPatchOperation toOperation(final DiffOperation op)
- {
- return new AddOperation(op.path.toString(), op.value);
- }
- },
- COPY
- {
- @Override
- JsonPatchOperation toOperation(final DiffOperation op)
- {
- return new CopyOperation(op.from.toString(), op.path.toString());
- }
- },
- MOVE
- {
- @Override
- JsonPatchOperation toOperation(final DiffOperation op)
- {
- return new MoveOperation(op.from.toString(), op.path.toString());
- }
- },
- REMOVE
- {
- @Override
- JsonPatchOperation toOperation(final DiffOperation op)
- {
- return new RemoveOperation(op.from.toString());
- }
- },
- REPLACE
- {
- @Override
- JsonPatchOperation toOperation(final DiffOperation op)
- {
- return new ReplaceOperation(op.from.toString(), op.value);
- }
- },
- ;
-
- abstract JsonPatchOperation toOperation(final DiffOperation op);
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/diff/DiffProcessor.java b/src/main/java/com/github/fge/jsonpatch/diff/DiffProcessor.java
deleted file mode 100644
index 299d29a1..00000000
--- a/src/main/java/com/github/fge/jsonpatch/diff/DiffProcessor.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch.diff;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.github.fge.jackson.JsonNumEquals;
-import com.github.fge.jackson.jsonpointer.JsonPointer;
-import com.github.fge.jsonpatch.JsonPatch;
-import com.github.fge.jsonpatch.JsonPatchOperation;
-
-import javax.annotation.Nullable;
-import java.util.*;
-
-// TODO: cleanup
-final class DiffProcessor
-{
- private static final JsonNumEquals EQUIVALENCE
- = JsonNumEquals.getInstance();
-
- private final Map unchanged;
-
- private final List diffs = new ArrayList();
-
- DiffProcessor(final Map unchanged)
- {
- this.unchanged = Collections.unmodifiableMap(new HashMap(unchanged));
- }
-
- void valueReplaced(final JsonPointer pointer, final JsonNode oldValue,
- final JsonNode newValue)
- {
- diffs.add(DiffOperation.replace(pointer, oldValue, newValue));
- }
-
- void valueRemoved(final JsonPointer pointer, final JsonNode value)
- {
- diffs.add(DiffOperation.remove(pointer, value));
- }
-
- void valueAdded(final JsonPointer pointer, final JsonNode value)
- {
- final int removalIndex = findPreviouslyRemoved(value);
- if (removalIndex != -1) {
- final DiffOperation removed = diffs.get(removalIndex);
- diffs.remove(removalIndex);
- diffs.add(DiffOperation.move(removed.getFrom(),
- value, pointer, value));
- return;
- }
- final JsonPointer ptr = findUnchangedValue(value);
- final DiffOperation op = ptr != null
- ? DiffOperation.copy(ptr, pointer, value)
- : DiffOperation.add(pointer, value);
-
- diffs.add(op);
- }
-
- JsonPatch getPatch()
- {
- final List list = new ArrayList();
-
- for (final DiffOperation op: diffs)
- list.add(op.asJsonPatchOperation());
-
- return new JsonPatch(list);
- }
-
- @Nullable
- private JsonPointer findUnchangedValue(final JsonNode value)
- {
- for (final Map.Entry entry: unchanged.entrySet())
- if (EQUIVALENCE.equivalent(value, entry.getValue()))
- return entry.getKey();
- return null;
- }
-
- private int findPreviouslyRemoved(final JsonNode value)
- {
- DiffOperation op;
-
- for (int i = 0; i < diffs.size(); i++) {
- op = diffs.get(i);
- if (op.getType() == DiffOperation.Type.REMOVE
- && EQUIVALENCE.equivalent(value, op.getOldValue()))
- return i;
- }
- return -1;
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/diff/JsonDiff.java b/src/main/java/com/github/fge/jsonpatch/diff/JsonDiff.java
deleted file mode 100644
index 302a79c2..00000000
--- a/src/main/java/com/github/fge/jsonpatch/diff/JsonDiff.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch.diff;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.github.fge.jackson.JacksonUtils;
-import com.github.fge.jackson.JsonNumEquals;
-import com.github.fge.jackson.NodeType;
-import com.github.fge.jackson.jsonpointer.JsonPointer;
-import com.github.fge.jsonpatch.JsonPatch;
-import com.github.fge.jsonpatch.JsonPatchMessages;
-import com.github.fge.msgsimple.bundle.MessageBundle;
-import com.github.fge.msgsimple.load.MessageBundles;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-import java.io.IOException;
-import java.util.*;
-
-/**
- * JSON "diff" implementation
- *
- *
This class generates a JSON Patch (as in, an RFC 6902 JSON Patch) given
- * two JSON values as inputs. The patch can be obtained directly as a {@link
- * JsonPatch} or as a {@link JsonNode}.
- *
- *
Note: there is no guarantee about the usability of the generated
- * patch for any other source/target combination than the one used to generate
- * the patch.
- *
- *
This class always performs operations in the following order: removals,
- * additions and replacements. It then factors removal/addition pairs into
- * move operations, or copy operations if a common element exists, at the same
- * {@link JsonPointer pointer}, in both the source and destination.
- *
- *
You can obtain a diff either as a {@link JsonPatch} directly or, for
- * backwards compatibility, as a {@link JsonNode}.
- *
- * @since 1.2
- */
-@ParametersAreNonnullByDefault
-public final class JsonDiff
-{
- private static final MessageBundle BUNDLE
- = MessageBundles.getBundle(JsonPatchMessages.class);
- private static final ObjectMapper MAPPER = JacksonUtils.newMapper();
-
- private static final JsonNumEquals EQUIVALENCE
- = JsonNumEquals.getInstance();
-
- private JsonDiff()
- {
- }
-
- /**
- * Generate a JSON patch for transforming the source node into the target
- * node
- *
- * @param source the node to be patched
- * @param target the expected result after applying the patch
- * @return the patch as a {@link JsonPatch}
- *
- * @since 1.9
- */
- public static JsonPatch asJsonPatch(final JsonNode source,
- final JsonNode target)
- {
- BUNDLE.checkNotNull(source, "common.nullArgument");
- BUNDLE.checkNotNull(target, "common.nullArgument");
- final Map unchanged
- = getUnchangedValues(source, target);
- final DiffProcessor processor = new DiffProcessor(unchanged);
-
- generateDiffs(processor, JsonPointer.empty(), source, target);
- return processor.getPatch();
- }
-
- /**
- * Generate a JSON patch for transforming the source node into the target
- * node
- *
- * @param source the node to be patched
- * @param target the expected result after applying the patch
- * @return the patch as a {@link JsonNode}
- */
- public static JsonNode asJson(final JsonNode source, final JsonNode target)
- {
- final String s;
- try {
- s = MAPPER.writeValueAsString(asJsonPatch(source, target));
- return MAPPER.readTree(s);
- } catch (IOException e) {
- throw new RuntimeException("cannot generate JSON diff", e);
- }
- }
-
- private static void generateDiffs(final DiffProcessor processor,
- final JsonPointer pointer, final JsonNode source, final JsonNode target)
- {
- if (EQUIVALENCE.equivalent(source, target))
- return;
-
- final NodeType firstType = NodeType.getNodeType(source);
- final NodeType secondType = NodeType.getNodeType(target);
-
- /*
- * Node types differ: generate a replacement operation.
- */
- if (firstType != secondType) {
- processor.valueReplaced(pointer, source, target);
- return;
- }
-
- /*
- * If we reach this point, it means that both nodes are the same type,
- * but are not equivalent.
- *
- * If this is not a container, generate a replace operation.
- */
- if (!source.isContainerNode()) {
- processor.valueReplaced(pointer, source, target);
- return;
- }
-
- /*
- * If we reach this point, both nodes are either objects or arrays;
- * delegate.
- */
- if (firstType == NodeType.OBJECT)
- generateObjectDiffs(processor, pointer, (ObjectNode) source,
- (ObjectNode) target);
- else // array
- generateArrayDiffs(processor, pointer, (ArrayNode) source,
- (ArrayNode) target);
- }
-
- private static void generateObjectDiffs(final DiffProcessor processor,
- final JsonPointer pointer, final ObjectNode source,
- final ObjectNode target)
- {
- final Set firstFields
- = collect(source.fieldNames(), new TreeSet());
- final Set secondFields
- = collect(target.fieldNames(), new TreeSet());
-
- final Set copy1 = new HashSet(firstFields);
- copy1.removeAll(secondFields);
-
- for (final String field: Collections.unmodifiableSet(copy1))
- processor.valueRemoved(pointer.append(field), source.get(field));
-
- final Set copy2 = new HashSet(secondFields);
- copy2.removeAll(firstFields);
-
-
- for (final String field: Collections.unmodifiableSet(copy2))
- processor.valueAdded(pointer.append(field), target.get(field));
-
- final Set intersection = new HashSet(firstFields);
- intersection.retainAll(secondFields);
-
- for (final String field: intersection)
- generateDiffs(processor, pointer.append(field), source.get(field),
- target.get(field));
- }
-
- private static Set collect(Iterator from, Set to) {
- if (from == null) {
- throw new NullPointerException();
- }
- if (to == null) {
- throw new NullPointerException();
- }
- while (from.hasNext()) {
- to.add(from.next());
- }
- return Collections.unmodifiableSet(to);
- }
-
-
-
- private static void generateArrayDiffs(final DiffProcessor processor,
- final JsonPointer pointer, final ArrayNode source,
- final ArrayNode target)
- {
- final int firstSize = source.size();
- final int secondSize = target.size();
- final int size = Math.min(firstSize, secondSize);
-
- /*
- * Source array is larger; in this case, elements are removed from the
- * target; the index of removal is always the original arrays's length.
- */
- for (int index = size; index < firstSize; index++)
- processor.valueRemoved(pointer.append(size), source.get(index));
-
- for (int index = 0; index < size; index++)
- generateDiffs(processor, pointer.append(index), source.get(index),
- target.get(index));
-
- // Deal with the destination array being larger...
- for (int index = size; index < secondSize; index++)
- processor.valueAdded(pointer.append("-"), target.get(index));
- }
-
-
- static Map getUnchangedValues(final JsonNode source,
- final JsonNode target)
- {
- final Map ret = new HashMap();
- computeUnchanged(ret, JsonPointer.empty(), source, target);
- return ret;
- }
-
- private static void computeUnchanged(final Map ret,
- final JsonPointer pointer, final JsonNode first, final JsonNode second)
- {
- if (EQUIVALENCE.equivalent(first, second)) {
- ret.put(pointer, second);
- return;
- }
-
- final NodeType firstType = NodeType.getNodeType(first);
- final NodeType secondType = NodeType.getNodeType(second);
-
- if (firstType != secondType)
- return; // nothing in common
-
- // We know they are both the same type, so...
-
- switch (firstType) {
- case OBJECT:
- computeObject(ret, pointer, first, second);
- break;
- case ARRAY:
- computeArray(ret, pointer, first, second);
- break;
- default:
- /* nothing */
- }
- }
-
- private static void computeObject(final Map ret,
- final JsonPointer pointer, final JsonNode source,
- final JsonNode target)
- {
- final Iterator firstFields = source.fieldNames();
-
- String name;
-
- while (firstFields.hasNext()) {
- name = firstFields.next();
- if (!target.has(name))
- continue;
- computeUnchanged(ret, pointer.append(name), source.get(name),
- target.get(name));
- }
- }
-
- private static void computeArray(final Map ret,
- final JsonPointer pointer, final JsonNode source, final JsonNode target)
- {
- final int size = Math.min(source.size(), target.size());
-
- for (int i = 0; i < size; i++)
- computeUnchanged(ret, pointer.append(i), source.get(i),
- target.get(i));
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/mergepatch/JsonMergePatchDeserializer.java b/src/main/java/com/github/fge/jsonpatch/mergepatch/JsonMergePatchDeserializer.java
deleted file mode 100644
index 27b15d08..00000000
--- a/src/main/java/com/github/fge/jsonpatch/mergepatch/JsonMergePatchDeserializer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch.mergepatch;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.ObjectCodec;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.NullNode;
-import com.github.fge.jackson.JacksonUtils;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-final class JsonMergePatchDeserializer
- extends JsonDeserializer
-{
- /*
- * FIXME! UGLY! HACK!
- *
- * We MUST have an ObjectCodec ready so that the parser in .deserialize()
- * can actually do something useful -- for instance, deserializing even a
- * JsonNode.
- *
- * Jackson does not do this automatically; I don't know why...
- */
- private static final ObjectCodec CODEC = JacksonUtils.newMapper();
-
- @Override
- public JsonMergePatch deserialize(final JsonParser jp,
- final DeserializationContext ctxt)
- throws IOException, JsonProcessingException
- {
- // FIXME: see comment above
- jp.setCodec(CODEC);
- final JsonNode node = jp.readValueAsTree();
-
- /*
- * Not an object: the simple case
- */
- if (!node.isObject())
- return new NonObjectMergePatch(node);
-
- /*
- * The complicated case...
- *
- * We have to build a set of removed members, plus a map of modified
- * members.
- */
-
- final Set removedMembers = new HashSet();
- final Map modifiedMembers = new HashMap();
- final Iterator> iterator = node.fields();
-
- Map.Entry entry;
-
- while (iterator.hasNext()) {
- entry = iterator.next();
- if (entry.getValue().isNull())
- removedMembers.add(entry.getKey());
- else {
- final JsonMergePatch value
- = deserialize(entry.getValue().traverse(), ctxt);
- modifiedMembers.put(entry.getKey(), value);
- }
- }
-
- return new ObjectMergePatch(removedMembers, modifiedMembers);
- }
-
- /*
- * This method MUST be overriden... The default is to return null, which is
- * not what we want.
- */
- @Override
- @SuppressWarnings("deprecation")
- public JsonMergePatch getNullValue()
- {
- return new NonObjectMergePatch(NullNode.getInstance());
- }
-}
diff --git a/src/main/java/com/github/fge/jsonpatch/mergepatch/ObjectMergePatch.java b/src/main/java/com/github/fge/jsonpatch/mergepatch/ObjectMergePatch.java
deleted file mode 100644
index 009c3347..00000000
--- a/src/main/java/com/github/fge/jsonpatch/mergepatch/ObjectMergePatch.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
- *
- * This software is dual-licensed under:
- *
- * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
- * later version;
- * - the Apache Software License (ASL) version 2.0.
- *
- * The text of this file and of both licenses is available at the root of this
- * project or, if you have the jar distribution, in directory META-INF/, under
- * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
- *
- * Direct link to the sources:
- *
- * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
- */
-
-package com.github.fge.jsonpatch.mergepatch;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
-import com.fasterxml.jackson.databind.node.NullNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.github.fge.jackson.JacksonUtils;
-import com.github.fge.jsonpatch.JsonPatchException;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-@ParametersAreNonnullByDefault
-final class ObjectMergePatch
- extends JsonMergePatch
-{
- private final Set removedMembers;
- private final Map modifiedMembers;
-
- ObjectMergePatch(final Set removedMembers,
- final Map modifiedMembers)
- {
- this.removedMembers = Collections.unmodifiableSet(new HashSet(removedMembers));
- this.modifiedMembers = Collections.unmodifiableMap(new HashMap(modifiedMembers));
- }
-
- @Override
- public JsonNode apply(final JsonNode input)
- throws JsonPatchException
- {
- BUNDLE.checkNotNull(input, "jsonPatch.nullValue");
- /*
- * If the input is an object, we make a deep copy of it
- */
- final ObjectNode ret = input.isObject() ? (ObjectNode) input.deepCopy()
- : JacksonUtils.nodeFactory().objectNode();
-
- /*
- * Our result is now a JSON Object; first, add (or modify) existing
- * members in the result
- */
- String key;
- JsonNode value;
- for (final Map.Entry entry:
- modifiedMembers.entrySet()) {
- key = entry.getKey();
- /*
- * FIXME: ugly...
- *
- * We treat missing keys as null nodes; this "works" because in
- * the modifiedMembers map, values are JsonMergePatch instances:
- *
- * * if it is a NonObjectMergePatch, the value is replaced
- * unconditionally;
- * * if it is an ObjectMergePatch, we get back here; the value will
- * be replaced with a JSON Object anyway before being processed.
- */
- final JsonNode jsonNode = ret.get(key);
- value = jsonNode != null ? jsonNode : NullNode.getInstance();
- ret.replace(key, entry.getValue().apply(value));
- }
-
- ret.remove(removedMembers);
-
- return ret;
- }
-
- @Override
- public void serialize(final JsonGenerator jgen,
- final SerializerProvider provider)
- throws IOException, JsonProcessingException
- {
- jgen.writeStartObject();
-
- /*
- * Write removed members as JSON nulls
- */
- for (final String member: removedMembers)
- jgen.writeNullField(member);
-
- /*
- * Write modified members; delegate to serialization for writing values
- */
- for (final Map.Entry entry:
- modifiedMembers.entrySet()) {
- jgen.writeFieldName(entry.getKey());
- entry.getValue().serialize(jgen, provider);
- }
-
- jgen.writeEndObject();
- }
-
- @Override
- public void serializeWithType(final JsonGenerator jgen,
- final SerializerProvider provider, final TypeSerializer typeSer)
- throws IOException, JsonProcessingException
- {
- serialize(jgen, provider);
- }
-}
diff --git a/src/main/java/com/gravity9/jsonpatch/AddOperation.java b/src/main/java/com/gravity9/jsonpatch/AddOperation.java
new file mode 100644
index 00000000..9a7fb5c1
--- /dev/null
+++ b/src/main/java/com/gravity9/jsonpatch/AddOperation.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
+ *
+ * This software is dual-licensed under:
+ *
+ * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
+ * later version;
+ * - the Apache Software License (ASL) version 2.0.
+ *
+ * The text of this file and of both licenses is available at the root of this
+ * project or, if you have the jar distribution, in directory META-INF/, under
+ * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
+ *
+ * Direct link to the sources:
+ *
+ * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
+ * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+
+package com.gravity9.jsonpatch;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.jayway.jsonpath.DocumentContext;
+import com.jayway.jsonpath.JsonPath;
+
+
+/**
+ * JSON Patch {@code add} operation
+ *
+ *
For this operation, {@code path} is the JSON Pointer where the value
+ * should be added, and {@code value} is the value to add.
+ *
+ *
Note that if the target value pointed to by {@code path} already exists,
+ * it is replaced. In this case, {@code add} is equivalent to {@code replace}.
+ *
+ *
+ *
Note also that a value will be created at the target path if and only
+ * if the immediate parent of that value exists (and is of the correct
+ * type).
+ *
+ *
Finally, if the last reference token of the JSON Pointer is {@code -} and
+ * the immediate parent is an array, the given value is added at the end of the
+ * array. For instance, applying:
+ *
+ *
+ * { "op": "add", "path": "/-", "value": 3 }
+ *
+ *
+ *
to:
+ *
+ *
+ * [ 1, 2 ]
+ *
+ *
+ *
will give:
+ *
+ *
+ * [ 1, 2, 3 ]
+ *
+ */
+public final class AddOperation extends PathValueOperation {
+
+ public static final String LAST_ARRAY_ELEMENT_SYMBOL = "-";
+
+ @JsonCreator
+ public AddOperation(@JsonProperty("path") final String path,
+ @JsonProperty("value") final JsonNode value) {
+ super("add", path, value);
+ }
+
+ @Override
+ public JsonNode applyInternal(final JsonNode node) throws JsonPatchException {
+ if (path.isEmpty()) {
+ return value;
+ }
+ PathDetails pathDetails = PathParser.getParentPathAndNewNodeName(path);
+ final String pathToParent = pathDetails.getPathToParent();
+ final String newNodeName = pathDetails.getNewNodeName();
+
+ final DocumentContext nodeContext = JsonPath.parse(node.deepCopy());
+ final JsonNode evaluatedJsonParents = nodeContext.read(pathToParent);
+ if (evaluatedJsonParents == null) {
+ throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchParent"));
+ }
+ if (!evaluatedJsonParents.isContainerNode()) {
+ throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer"));
+ }
+
+ if (pathDetails.doesContainFiltersOrMultiIndexesNotation()) { // json filter result is always a list
+ for (int i = 0; i < evaluatedJsonParents.size(); i++) {
+ JsonNode parentNode = evaluatedJsonParents.get(i);
+ if (!parentNode.isContainerNode()) {
+ throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer"));
+ }
+ DocumentContext containerContext = JsonPath.parse(parentNode);
+ if (parentNode.isArray()) {
+ addToArray(containerContext, "$", newNodeName);
+ } else {
+ addToObject(containerContext, "$", newNodeName);
+ }
+ }
+ return nodeContext.read("$");
+ } else {
+ return evaluatedJsonParents.isArray()
+ ? addToArray(nodeContext, pathToParent, newNodeName)
+ : addToObject(nodeContext, pathToParent, newNodeName);
+ }
+ }
+
+ private JsonNode addToArray(final DocumentContext node, String jsonPath, String newNodeName) throws JsonPatchException {
+ if (newNodeName.equals(LAST_ARRAY_ELEMENT_SYMBOL)) {
+ return node.add(jsonPath, value).read("$", JsonNode.class);
+ }
+
+ final int size = node.read(jsonPath, JsonNode.class).size();
+ final int index = verifyAndGetArrayIndex(newNodeName, size);
+
+ ArrayNode updatedArray = node.read(jsonPath, ArrayNode.class).insert(index, value);
+ return "$".equals(jsonPath) ? updatedArray : node.set(jsonPath, updatedArray).read("$", JsonNode.class);
+ }
+
+ private JsonNode addToObject(final DocumentContext node, String jsonPath, String newNodeName) {
+ return node
+ .put(jsonPath, newNodeName, value)
+ .read("$", JsonNode.class);
+ }
+
+ private int verifyAndGetArrayIndex(String stringIndex, int size) throws JsonPatchException {
+ int index;
+ try {
+ index = Integer.parseInt(stringIndex);
+ } catch (NumberFormatException ignored) {
+ throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.notAnIndex"));
+ }
+ if (index < 0 || index > size) {
+ throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchIndex"));
+ }
+ return index;
+ }
+}
diff --git a/src/main/java/com/github/fge/jsonpatch/CopyOperation.java b/src/main/java/com/gravity9/jsonpatch/CopyOperation.java
similarity index 69%
rename from src/main/java/com/github/fge/jsonpatch/CopyOperation.java
rename to src/main/java/com/gravity9/jsonpatch/CopyOperation.java
index c35f5dac..9d4f669e 100644
--- a/src/main/java/com/github/fge/jsonpatch/CopyOperation.java
+++ b/src/main/java/com/gravity9/jsonpatch/CopyOperation.java
@@ -17,7 +17,7 @@
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/
-package com.github.fge.jsonpatch;
+package com.gravity9.jsonpatch;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -42,18 +42,18 @@
*/
public final class CopyOperation extends DualPathOperation {
- @JsonCreator
- public CopyOperation(@JsonProperty("from") final String from, @JsonProperty("path") final String path) {
- super("copy", from, path);
- }
+ @JsonCreator
+ public CopyOperation(@JsonProperty("from") final String from, @JsonProperty("path") final String path) {
+ super("copy", from, path);
+ }
- @Override
- public JsonNode applyInternal(final JsonNode node) throws JsonPatchException {
- final String jsonPath = JsonPathParser.tmfStringToJsonPath(from);
- final JsonNode dupData = JsonPath.parse(node.deepCopy()).read(jsonPath);
- if (dupData == null) {
- throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchPath"));
- }
- return new AddOperation(path, dupData).apply(node);
- }
+ @Override
+ public JsonNode applyInternal(final JsonNode node) throws JsonPatchException {
+ final String jsonPath = JsonPathParser.tmfStringToJsonPath(from);
+ final JsonNode dupData = JsonPath.parse(node.deepCopy()).read(jsonPath);
+ if (dupData == null) {
+ throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchPath"));
+ }
+ return new AddOperation(path, dupData).apply(node);
+ }
}
diff --git a/src/main/java/com/gravity9/jsonpatch/DualPathOperation.java b/src/main/java/com/gravity9/jsonpatch/DualPathOperation.java
new file mode 100644
index 00000000..15946096
--- /dev/null
+++ b/src/main/java/com/gravity9/jsonpatch/DualPathOperation.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
+ *
+ * This software is dual-licensed under:
+ *
+ * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
+ * later version;
+ * - the Apache Software License (ASL) version 2.0.
+ *
+ * The text of this file and of both licenses is available at the root of this
+ * project or, if you have the jar distribution, in directory META-INF/, under
+ * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
+ *
+ * Direct link to the sources:
+ *
+ * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
+ * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
+ */
+
+package com.gravity9.jsonpatch;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import java.io.IOException;
+
+/**
+ * Base class for JSON Patch operations taking two JSON Pointers as arguments
+ */
+public abstract class DualPathOperation extends JsonPatchOperation {
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ protected final String from;
+
+ /**
+ * Protected constructor
+ *
+ * @param op operation name
+ * @param from source path
+ * @param path destination path
+ */
+ protected DualPathOperation(final String op, final String from, final String path) {
+ super(op, path);
+ this.from = from;
+ }
+
+ @Override
+ public final void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonProcessingException {
+ jgen.writeStartObject();
+ jgen.writeStringField("op", op);
+ jgen.writeStringField("path", path);
+ jgen.writeStringField("from", from);
+ jgen.writeEndObject();
+ }
+
+ @Override
+ public final void serializeWithType(final JsonGenerator jgen, final SerializerProvider provider,
+ final TypeSerializer typeSer) throws IOException, JsonProcessingException {
+ serialize(jgen, provider);
+ }
+
+ public final String getFrom() {
+ return from;
+ }
+
+ @Override
+ public final String toString() {
+ return "op: " + op + "; from: \"" + from + "\"; path: \"" + path + '"';
+ }
+}
diff --git a/src/main/java/com/gravity9/jsonpatch/Iterables.java b/src/main/java/com/gravity9/jsonpatch/Iterables.java
new file mode 100644
index 00000000..3e6ce222
--- /dev/null
+++ b/src/main/java/com/gravity9/jsonpatch/Iterables.java
@@ -0,0 +1,33 @@
+package com.gravity9.jsonpatch;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterables utility class
+ *
+ * @author {@literal @}soberich on 30-Nov-18
+ */
+public final class Iterables {
+
+ private Iterables() {
+ }
+
+ /**
+ * Returns the last element of {@code iterable}.
+ *
+ * @param underlying type being iterated
+ * @param iterable type of iterable
+ * @return the last element of {@code iterable}
+ * @throws NoSuchElementException if the iterable is empty
+ */
+ public static T getLast(Iterable iterable) {
+ Iterator iterator = iterable.iterator();
+ while (true) {
+ T current = iterator.next();
+ if (!iterator.hasNext()) {
+ return current;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPatch.java b/src/main/java/com/gravity9/jsonpatch/JsonPatch.java
similarity index 55%
rename from src/main/java/com/github/fge/jsonpatch/JsonPatch.java
rename to src/main/java/com/gravity9/jsonpatch/JsonPatch.java
index b41ed6bb..e656806b 100644
--- a/src/main/java/com/github/fge/jsonpatch/JsonPatch.java
+++ b/src/main/java/com/gravity9/jsonpatch/JsonPatch.java
@@ -17,7 +17,7 @@
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/
-package com.github.fge.jsonpatch;
+package com.gravity9.jsonpatch;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -28,7 +28,6 @@
import com.github.fge.jackson.JacksonUtils;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.load.MessageBundles;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -90,93 +89,86 @@
*
IMPORTANT NOTE: the JSON Patch is supposed to be VALID when the
* constructor for this class ({@link JsonPatch#fromJson(JsonNode)} is used.
*/
-public final class JsonPatch
- implements JsonSerializable, Patch
-{
- private static final MessageBundle BUNDLE
- = MessageBundles.getBundle(JsonPatchMessages.class);
+public final class JsonPatch implements JsonSerializable, Patch {
+
+ private static final MessageBundle BUNDLE
+ = MessageBundles.getBundle(JsonPatchMessages.class);
- /**
- * List of operations
- */
- private final List operations;
+ /**
+ * List of operations
+ */
+ private final List operations;
- /**
- * Constructor
- *
- *
Normally, you should never have to use it.
- *
- * @param operations the list of operations for this patch
- * @see JsonPatchOperation
- */
- @JsonCreator
- public JsonPatch(final List operations)
- {
- this.operations = Collections.unmodifiableList(new ArrayList(operations));
- }
+ /**
+ * Constructor
+ *
+ *
Normally, you should never have to use it.
+ *
+ * @param operations the list of operations for this patch
+ * @see JsonPatchOperation
+ */
+ @JsonCreator
+ public JsonPatch(final List operations) {
+ this.operations = Collections.unmodifiableList(new ArrayList(operations));
+ }
- /**
- * Static factory method to build a JSON Patch out of a JSON representation
- *
- * @param node the JSON representation of the generated JSON Patch
- * @return a JSON Patch
- * @throws IOException input is not a valid JSON patch
- * @throws NullPointerException input is null
- */
- public static JsonPatch fromJson(final JsonNode node)
- throws IOException
- {
- BUNDLE.checkNotNull(node, "jsonPatch.nullInput");
- return JacksonUtils.getReader().forType(JsonPatch.class)
- .readValue(node);
- }
+ /**
+ * Static factory method to build a JSON Patch out of a JSON representation
+ *
+ * @param node the JSON representation of the generated JSON Patch
+ * @return a JSON Patch
+ * @throws IOException input is not a valid JSON patch
+ * @throws NullPointerException input is null
+ */
+ public static JsonPatch fromJson(final JsonNode node)
+ throws IOException {
+ BUNDLE.checkNotNull(node, "jsonPatch.nullInput");
+ return JacksonUtils.getReader().forType(JsonPatch.class)
+ .readValue(node);
+ }
- /**
- * Apply this patch to a JSON value
- *
- * @param node the value to apply the patch to
- * @return the patched JSON value
- * @throws JsonPatchException failed to apply patch
- * @throws NullPointerException input is null
- */
- @Override
- public JsonNode apply(final JsonNode node)
- throws JsonPatchException
- {
- BUNDLE.checkNotNull(node, "jsonPatch.nullInput");
- JsonNode ret = node;
- for (final JsonPatchOperation operation: operations)
- ret = operation.apply(ret);
+ /**
+ * Apply this patch to a JSON value
+ *
+ * @param node the value to apply the patch to
+ * @return the patched JSON value
+ * @throws JsonPatchException failed to apply patch
+ * @throws NullPointerException input is null
+ */
+ @Override
+ public JsonNode apply(final JsonNode node)
+ throws JsonPatchException {
+ BUNDLE.checkNotNull(node, "jsonPatch.nullInput");
+ JsonNode ret = node;
+ for (final JsonPatchOperation operation : operations)
+ ret = operation.apply(ret);
- return ret;
- }
+ return ret;
+ }
- public final List getOperations() {
- return operations;
- }
+ public final List getOperations() {
+ return operations;
+ }
- @Override
- public String toString()
- {
- return operations.toString();
- }
+ @Override
+ public String toString() {
+ return operations.toString();
+ }
- @Override
- public void serialize(final JsonGenerator jgen,
- final SerializerProvider provider)
- throws IOException
- {
- jgen.writeStartArray();
- for (final JsonPatchOperation op: operations)
- op.serialize(jgen, provider);
- jgen.writeEndArray();
- }
+ @Override
+ public void serialize(final JsonGenerator jgen,
+ final SerializerProvider provider)
+ throws IOException {
+ jgen.writeStartArray();
+ for (final JsonPatchOperation op : operations)
+ op.serialize(jgen, provider);
+ jgen.writeEndArray();
+ }
- @Override
- public void serializeWithType(final JsonGenerator jgen,
- final SerializerProvider provider, final TypeSerializer typeSer)
- throws IOException
- {
- serialize(jgen, provider);
- }
+ @Override
+ public void serializeWithType(final JsonGenerator jgen,
+ final SerializerProvider provider, final TypeSerializer typeSer)
+ throws IOException {
+ serialize(jgen, provider);
+ }
}
diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPatchException.java b/src/main/java/com/gravity9/jsonpatch/JsonPatchException.java
similarity index 68%
rename from src/main/java/com/github/fge/jsonpatch/JsonPatchException.java
rename to src/main/java/com/gravity9/jsonpatch/JsonPatchException.java
index 088f1b87..15976291 100644
--- a/src/main/java/com/github/fge/jsonpatch/JsonPatchException.java
+++ b/src/main/java/com/gravity9/jsonpatch/JsonPatchException.java
@@ -17,18 +17,15 @@
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/
-package com.github.fge.jsonpatch;
+package com.gravity9.jsonpatch;
-public final class JsonPatchException
- extends Exception
-{
- public JsonPatchException(final String message)
- {
- super(message);
- }
+public final class JsonPatchException extends Exception {
- public JsonPatchException(final String message, final Throwable cause)
- {
- super(message, cause);
- }
+ public JsonPatchException(final String message) {
+ super(message);
+ }
+
+ public JsonPatchException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPatchMessages.java b/src/main/java/com/gravity9/jsonpatch/JsonPatchMessages.java
similarity index 76%
rename from src/main/java/com/github/fge/jsonpatch/JsonPatchMessages.java
rename to src/main/java/com/gravity9/jsonpatch/JsonPatchMessages.java
index a67583bf..1dec9c48 100644
--- a/src/main/java/com/github/fge/jsonpatch/JsonPatchMessages.java
+++ b/src/main/java/com/gravity9/jsonpatch/JsonPatchMessages.java
@@ -17,18 +17,16 @@
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/
-package com.github.fge.jsonpatch;
+package com.gravity9.jsonpatch;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.bundle.PropertiesBundle;
import com.github.fge.msgsimple.load.MessageBundleLoader;
-public final class JsonPatchMessages
- implements MessageBundleLoader
-{
- @Override
- public MessageBundle getBundle()
- {
- return PropertiesBundle.forPath("/com/github/fge/jsonpatch/messages");
- }
+public final class JsonPatchMessages implements MessageBundleLoader {
+
+ @Override
+ public MessageBundle getBundle() {
+ return PropertiesBundle.forPath("/com/github/fge/jsonpatch/messages");
+ }
}
diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java b/src/main/java/com/gravity9/jsonpatch/JsonPatchOperation.java
similarity index 50%
rename from src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java
rename to src/main/java/com/gravity9/jsonpatch/JsonPatchOperation.java
index a94808d1..72f3f73d 100644
--- a/src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java
+++ b/src/main/java/com/gravity9/jsonpatch/JsonPatchOperation.java
@@ -17,7 +17,7 @@
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/
-package com.github.fge.jsonpatch;
+package com.gravity9.jsonpatch;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
@@ -43,12 +43,12 @@
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "op")
@JsonSubTypes({
- @Type(name = "add", value = AddOperation.class),
- @Type(name = "copy", value = CopyOperation.class),
- @Type(name = "move", value = MoveOperation.class),
- @Type(name = "remove", value = RemoveOperation.class),
- @Type(name = "replace", value = ReplaceOperation.class),
- @Type(name = "test", value = TestOperation.class)
+ @Type(name = "add", value = AddOperation.class),
+ @Type(name = "copy", value = CopyOperation.class),
+ @Type(name = "move", value = MoveOperation.class),
+ @Type(name = "remove", value = RemoveOperation.class),
+ @Type(name = "replace", value = ReplaceOperation.class),
+ @Type(name = "test", value = TestOperation.class)
})
/*
@@ -67,73 +67,74 @@
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class JsonPatchOperation implements JsonSerializable {
- protected static final MessageBundle BUNDLE = MessageBundles.getBundle(JsonPatchMessages.class);
-
- static {
- Configuration.setDefaults(new Configuration.Defaults() {
- @Override
- public JsonProvider jsonProvider() {
- return new JacksonJsonNodeJsonProvider();
- }
-
- @Override
- public Set