21
21
import com .fasterxml .jackson .databind .node .ObjectNode ;
22
22
23
23
import java .util .EnumSet ;
24
- import java .util .List ;
25
24
26
25
class InPlaceApplyProcessor implements JsonPatchProcessor {
27
26
@@ -42,187 +41,123 @@ public JsonNode result() {
42
41
}
43
42
44
43
@ Override
45
- public void move (List <String > fromPath , List <String > toPath ) {
46
- JsonNode parentNode = getParentNode (fromPath , Operation .MOVE );
47
- String field = fromPath .get (fromPath .size () - 1 ).replaceAll ("\" " , "" );
48
- JsonNode valueNode = parentNode .isArray () ? parentNode .get (Integer .parseInt (field )) : parentNode .get (field );
44
+ public void move (JsonPointer fromPath , JsonPointer toPath ) throws JsonPointerEvaluationException {
45
+ JsonNode valueNode = fromPath .evaluate (target );
49
46
remove (fromPath );
50
- add (toPath , valueNode );
47
+ set (toPath , valueNode , Operation . MOVE );
51
48
}
52
49
53
50
@ Override
54
- public void copy (List <String > fromPath , List <String > toPath ) {
55
- JsonNode parentNode = getParentNode (fromPath , Operation .COPY );
56
- String field = fromPath .get (fromPath .size () - 1 ).replaceAll ("\" " , "" );
57
- JsonNode valueNode = parentNode .isArray () ? parentNode .get (Integer .parseInt (field )) : parentNode .get (field );
51
+ public void copy (JsonPointer fromPath , JsonPointer toPath ) throws JsonPointerEvaluationException {
52
+ JsonNode valueNode = fromPath .evaluate (target );
58
53
JsonNode valueToCopy = valueNode != null ? valueNode .deepCopy () : null ;
59
- add (toPath , valueToCopy );
54
+ set (toPath , valueToCopy , Operation . COPY );
60
55
}
61
56
62
- @ Override
63
- public void test (List <String > path , JsonNode value ) {
64
- if (path .isEmpty ()) {
65
- error (Operation .TEST , "path is empty , path : " );
66
- } else {
67
- JsonNode parentNode = getParentNode (path , Operation .TEST );
68
- String fieldToReplace = path .get (path .size () - 1 ).replaceAll ("\" " , "" );
69
- if (fieldToReplace .equals ("" ) && path .size () == 1 )
70
- if (target .equals (value )) {
71
- target = value ;
72
- } else {
73
- error (Operation .TEST , "value mismatch" );
74
- }
75
- else if (!parentNode .isContainerNode ())
76
- error (Operation .TEST , "parent is not a container in source, path provided : " + PathUtils .getPathRepresentation (path ) + " | node : " + parentNode );
77
- else if (parentNode .isArray ()) {
78
- final ArrayNode target = (ArrayNode ) parentNode ;
79
- String idxStr = path .get (path .size () - 1 );
80
-
81
- if ("-" .equals (idxStr )) {
82
- // see http://tools.ietf.org/html/rfc6902#section-4.1
83
- if (!target .get (target .size () - 1 ).equals (value )) {
84
- error (Operation .TEST , "value mismatch" );
85
- }
86
- } else {
87
- int idx = arrayIndex (idxStr .replaceAll ("\" " , "" ), target .size (), false );
88
- if (!target .get (idx ).equals (value )) {
89
- error (Operation .TEST , "value mismatch" );
90
- }
91
- }
92
- } else {
93
- final ObjectNode target = (ObjectNode ) parentNode ;
94
- String key = path .get (path .size () - 1 ).replaceAll ("\" " , "" );
95
- JsonNode actual = target .get (key );
96
- if (actual == null )
97
- error (Operation .TEST , "noSuchPath in source, path provided : " + PathUtils .getPathRepresentation (path ));
98
- else if (!actual .equals (value ))
99
- error (Operation .TEST , "value mismatch" );
100
- }
101
- }
57
+ private static String show (JsonNode value ) {
58
+ if (value == null || value .isNull ())
59
+ return "null" ;
60
+ else if (value .isArray ())
61
+ return "array" ;
62
+ else if (value .isObject ())
63
+ return "object" ;
64
+ else
65
+ return "value " + value .toString (); // Caveat: numeric may differ from source (e.g. trailing zeros)
102
66
}
103
67
104
68
@ Override
105
- public void add (List <String > path , JsonNode value ) {
106
- if (path .isEmpty ()) {
107
- error (Operation .ADD , "path is empty , path : " );
108
- } else {
109
- JsonNode parentNode = getParentNode (path , Operation .ADD );
110
- String fieldToReplace = path .get (path .size () - 1 ).replaceAll ("\" " , "" );
111
- if (fieldToReplace .equals ("" ) && path .size () == 1 )
112
- target = value ;
113
- else if (!parentNode .isContainerNode ())
114
- error (Operation .ADD , "parent is not a container in source, path provided : " + PathUtils .getPathRepresentation (path ) + " | node : " + parentNode );
115
- else if (parentNode .isArray ())
116
- addToArray (path , value , parentNode );
117
- else
118
- addToObject (path , parentNode , value );
119
- }
69
+ public void test (JsonPointer path , JsonNode value ) throws JsonPointerEvaluationException {
70
+ JsonNode valueNode = path .evaluate (target );
71
+ if (!valueNode .equals (value ))
72
+ throw new JsonPatchApplicationException (
73
+ "Expected " + show (value ) + " but found " + show (valueNode ), Operation .TEST , path );
120
74
}
121
75
122
- private void addToObject (List <String > path , JsonNode node , JsonNode value ) {
123
- final ObjectNode target = (ObjectNode ) node ;
124
- String key = path .get (path .size () - 1 ).replaceAll ("\" " , "" );
125
- target .set (key , value );
76
+ @ Override
77
+ public void add (JsonPointer path , JsonNode value ) throws JsonPointerEvaluationException {
78
+ set (path , value , Operation .ADD );
126
79
}
127
80
128
- private void addToArray (List <String > path , JsonNode value , JsonNode parentNode ) {
129
- final ArrayNode target = (ArrayNode ) parentNode ;
130
- String idxStr = path .get (path .size () - 1 );
81
+ @ Override
82
+ public void replace (JsonPointer path , JsonNode value ) throws JsonPointerEvaluationException {
83
+ if (path .isRoot ()) {
84
+ target = value ;
85
+ return ;
86
+ }
131
87
132
- if ("-" .equals (idxStr )) {
133
- // see http://tools.ietf.org/html/rfc6902#section-4.1
134
- target .add (value );
88
+ JsonNode parentNode = path .getParent ().evaluate (target );
89
+ JsonPointer .RefToken token = path .last ();
90
+ if (parentNode .isObject ()) {
91
+ if (!parentNode .has (token .getField ()))
92
+ throw new JsonPatchApplicationException (
93
+ "Missing field \" " + token .getField () + "\" " , Operation .REPLACE , path .getParent ());
94
+ ((ObjectNode ) parentNode ).replace (token .getField (), value );
95
+ } else if (parentNode .isArray ()) {
96
+ if (token .getIndex () >= parentNode .size ())
97
+ throw new JsonPatchApplicationException (
98
+ "Array index " + token .getIndex () + " out of bounds" , Operation .REPLACE , path .getParent ());
99
+ ((ArrayNode ) parentNode ).set (token .getIndex (), value );
135
100
} else {
136
- int idx = arrayIndex ( idxStr . replaceAll ( " \" " , "" ), target . size (), false );
137
- target . insert ( idx , value );
101
+ throw new JsonPatchApplicationException (
102
+ "Can't reference past scalar value" , Operation . REPLACE , path . getParent () );
138
103
}
139
104
}
140
105
141
106
@ Override
142
- public void replace (List <String > path , JsonNode value ) {
143
- if (path .isEmpty ()) {
144
- error (Operation .REPLACE , "path is empty" );
107
+ public void remove (JsonPointer path ) throws JsonPointerEvaluationException {
108
+ if (path .isRoot ())
109
+ throw new JsonPatchApplicationException ("Cannot remove document root" , Operation .REMOVE , path );
110
+
111
+ JsonNode parentNode = path .getParent ().evaluate (target );
112
+ JsonPointer .RefToken token = path .last ();
113
+ if (parentNode .isObject ())
114
+ ((ObjectNode ) parentNode ).remove (token .getField ());
115
+ else if (parentNode .isArray ()) {
116
+ if (!flags .contains (CompatibilityFlags .REMOVE_NONE_EXISTING_ARRAY_ELEMENT ) &&
117
+ token .getIndex () >= parentNode .size ())
118
+ throw new JsonPatchApplicationException (
119
+ "Array index " + token .getIndex () + " out of bounds" , Operation .REPLACE , path .getParent ());
120
+ ((ArrayNode ) parentNode ).remove (token .getIndex ());
145
121
} else {
146
- JsonNode parentNode = getParentNode (path , Operation .REPLACE );
147
- String fieldToReplace = path .get (path .size () - 1 ).replaceAll ("\" " , "" );
148
- if (isNullOrEmpty (fieldToReplace ) && path .size () == 1 )
149
- target = value ;
150
- else if (parentNode .isObject () && parentNode .has (fieldToReplace )) {
151
- ((ObjectNode ) parentNode ).replace (fieldToReplace , value );
152
- } else if (parentNode .isArray ())
153
- ((ArrayNode ) parentNode ).set (arrayIndex (fieldToReplace , parentNode .size () - 1 , false ), value );
154
- else
155
- error (Operation .REPLACE , "noSuchPath in source, path provided : " + PathUtils .getPathRepresentation (path ));
122
+ throw new JsonPatchApplicationException (
123
+ "Cannot reference past scalar value" , Operation .REPLACE , path .getParent ());
156
124
}
157
125
}
158
126
159
- @ Override
160
- public void remove ( List < String > path ) {
161
- if ( path . isEmpty ()) {
162
- error ( Operation . REMOVE , " path is empty" );
163
- } else {
164
- JsonNode parentNode = getParentNode ( path , Operation . REMOVE );
165
- String fieldToRemove = path .get ( path . size () - 1 ). replaceAll ( " \" " , "" );
166
- if (parentNode .isObject ())
167
- (( ObjectNode ) parentNode ). remove ( fieldToRemove );
127
+
128
+
129
+ private void set ( JsonPointer path , JsonNode value , Operation forOp ) throws JsonPointerEvaluationException {
130
+ if ( path . isRoot ())
131
+ target = value ;
132
+ else {
133
+ JsonNode parentNode = path .getParent (). evaluate ( target );
134
+ if (! parentNode .isContainerNode ())
135
+ throw new JsonPatchApplicationException ( "Cannot reference past scalar value" , forOp , path . getParent () );
168
136
else if (parentNode .isArray ())
169
- (( ArrayNode ) parentNode ). remove ( arrayIndex ( fieldToRemove , parentNode . size () - 1 , flags . contains ( CompatibilityFlags . REMOVE_NONE_EXISTING_ARRAY_ELEMENT )) );
137
+ addToArray ( path , value , parentNode );
170
138
else
171
- error ( Operation . REMOVE , "noSuchPath in source, path provided : " + PathUtils . getPathRepresentation ( path ) );
139
+ addToObject ( path , parentNode , value );
172
140
}
173
141
}
174
142
175
- private void error (Operation forOp , String message ) {
176
- throw new JsonPatchApplicationException ("[" + forOp + " Operation] " + message );
143
+ private void addToObject (JsonPointer path , JsonNode node , JsonNode value ) {
144
+ final ObjectNode target = (ObjectNode ) node ;
145
+ String key = path .last ().getField ();
146
+ target .set (key , value );
177
147
}
178
148
179
- private JsonNode getParentNode (List <String > fromPath , Operation forOp ) {
180
- List <String > pathToParent = fromPath .subList (0 , fromPath .size () - 1 ); // would never by out of bound, lets see
181
- JsonNode node = getNode (target , pathToParent , 1 );
182
- if (node == null )
183
- error (forOp , "noSuchPath in source, path provided: " + PathUtils .getPathRepresentation (fromPath ));
184
- return node ;
185
- }
149
+ private void addToArray (JsonPointer path , JsonNode value , JsonNode parentNode ) {
150
+ final ArrayNode target = (ArrayNode ) parentNode ;
151
+ int idx = path .last ().getIndex ();
186
152
187
- private JsonNode getNode (JsonNode ret , List <String > path , int pos ) {
188
- if (pos >= path .size ()) {
189
- return ret ;
190
- }
191
- String key = path .get (pos );
192
- if (ret .isArray ()) {
193
- int keyInt = Integer .parseInt (key .replaceAll ("\" " , "" ));
194
- JsonNode element = ret .get (keyInt );
195
- if (element == null )
196
- return null ;
197
- else
198
- return getNode (ret .get (keyInt ), path , ++pos );
199
- } else if (ret .isObject ()) {
200
- if (ret .has (key )) {
201
- return getNode (ret .get (key ), path , ++pos );
202
- }
203
- return null ;
153
+ if (idx == JsonPointer .LAST_INDEX ) {
154
+ // see http://tools.ietf.org/html/rfc6902#section-4.1
155
+ target .add (value );
204
156
} else {
205
- return ret ;
206
- }
207
- }
208
-
209
- private int arrayIndex (String s , int max , boolean allowNoneExisting ) {
210
- int index ;
211
- try {
212
- index = Integer .parseInt (s );
213
- } catch (NumberFormatException nfe ) {
214
- throw new JsonPatchApplicationException ("Object operation on array target" );
215
- }
216
- if (index < 0 ) {
217
- throw new JsonPatchApplicationException ("index Out of bound, index is negative" );
218
- } else if (index > max ) {
219
- if (!allowNoneExisting )
220
- throw new JsonPatchApplicationException ("index Out of bound, index is greater than " + max );
157
+ if (idx > target .size ())
158
+ throw new JsonPatchApplicationException (
159
+ "Array index " + idx + " out of bounds" , Operation .ADD , path .getParent ());
160
+ target .insert (idx , value );
221
161
}
222
- return index ;
223
- }
224
-
225
- private boolean isNullOrEmpty (String string ) {
226
- return string == null || string .length () == 0 ;
227
162
}
228
163
}
0 commit comments