-
Notifications
You must be signed in to change notification settings - Fork 192
/
Copy pathAddOperation.java
154 lines (140 loc) · 4.95 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
152
153
154
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
* Copyright (c) 2015, Daisuke Miyamoto ([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.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder;
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 com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
/**
* 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())
throw new JsonPatchException(BUNDLE.getMessage(
"jsonPatch.noSuchParent"));
if (!parentNode.isContainerNode())
throw new JsonPatchException(BUNDLE.getMessage(
"jsonPatch.parentNotContainer"));
return parentNode.isArray()
? addToArray(path, node)
: addToObject(path, node);
}
@Override
public void applyToBuilder(ExpressionSpecBuilder builder) {
final TokenResolver<JsonNode> node = Iterators.getLast(path.iterator(), null /*default*/);
if(null == node || "-".equals(node.getToken().getRaw())) {
//list_append
throw new UnsupportedOperationException("list_append not supported yet");
} else {
super.applyToBuilder(builder);
}
}
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(BUNDLE.getMessage(
"jsonPatch.noSuchIndex"));
target.insert(index, value);
return ret;
}
private JsonNode addToObject(final JsonPointer path, final JsonNode node)
{
final JsonNode ret = node.deepCopy();
final ObjectNode target = (ObjectNode) path.parent().get(ret);
target.set(Iterables.getLast(path).getToken().getRaw(), value);
return ret;
}
}