-
Notifications
You must be signed in to change notification settings - Fork 192
/
Copy pathAddOperation.java
151 lines (136 loc) · 4.67 KB
/
AddOperation.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
*
* 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.fasterxml.jackson.databind.node.ObjectNode;
import com.github.fge.jackson.jsonpointer.JsonPointer;
import com.github.fge.jackson.jsonpointer.ReferenceToken;
import com.github.fge.jackson.jsonpointer.TokenResolver;
import java.util.NoSuchElementException;
/**
* JSON Patch {@code add} operation
*
* <p>For this operation, {@code path} is the JSON Pointer where the value
* should be added, and {@code value} is the value to add.</p>
*
* <p>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}.
* </p>
*
* <p>Note also that a value will be created at the target path <b>if and only
* if</b> the immediate parent of that value exists (and is of the correct
* type).</p>
*
* <p>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:</p>
*
* <pre>
* { "op": "add", "path": "/-", "value": 3 }
* </pre>
*
* <p>to:</p>
*
* <pre>
* [ 1, 2 ]
* </pre>
*
* <p>will give:</p>
*
* <pre>
* [ 1, 2, 3 ]
* </pre>
*/
public final class AddOperation
extends PathValueOperation
{
private static final ReferenceToken LAST_ARRAY_ELEMENT
= ReferenceToken.fromRaw("-");
@JsonCreator
public AddOperation(@JsonProperty("path") final JsonPointer path,
@JsonProperty("value") final JsonNode value)
{
super("add", path, value);
}
@Override
public JsonNode apply(final JsonNode node)
throws JsonPatchException
{
if (path.isEmpty())
return value;
/*
* Check the parent node: it must exist and be a container (ie an array
* or an object) for the add operation to work.
*/
final JsonNode parentNode = path.parent().path(node);
if (parentNode.isMissingNode())
{
String msg = BUNDLE.getMessage("jsonPatch.noSuchParent");
if(node!=null) {
msg = node.toString() + " " + msg;
}
throw new JsonPatchException(msg);
}
if (!parentNode.isContainerNode()) {
String msg = BUNDLE.getMessage("jsonPatch.parentNotContainer");
if(node!=null) {
msg = node.toString() + " " + msg;
}
throw new JsonPatchException(msg);
}
return parentNode.isArray()
? addToArray(path, node)
: addToObject(path, node);
}
private JsonNode addToArray(final JsonPointer path, final JsonNode node)
throws JsonPatchException
{
final JsonNode ret = node.deepCopy();
final ArrayNode target = (ArrayNode) path.parent().get(ret);
final TokenResolver<JsonNode> token = Iterables.getLast(path);
if (token.getToken().equals(LAST_ARRAY_ELEMENT)) {
target.add(value);
return ret;
}
final int size = target.size();
final int index;
try {
index = Integer.parseInt(token.toString());
} catch (NumberFormatException ignored) {
throw new JsonPatchException("[] " + BUNDLE.getMessage(
"jsonPatch.notAnIndex"));
}
if (index < 0 || index > size)
throw new JsonPatchException(node.toString() + " " + BUNDLE.getMessage(
"jsonPatch.noSuchIndex"));
target.insert(index, value);
return ret;
}
private JsonNode addToObject(final JsonPointer path, final JsonNode node)
{
final TokenResolver<JsonNode> token = Iterables.getLast(path);
final JsonNode ret = node.deepCopy();
final ObjectNode target = (ObjectNode) path.parent().get(ret);
target.set(token.getToken().getRaw(), value);
return ret;
}
}