31
31
32
32
public final class JsonDiff {
33
33
34
- private JsonDiff () {
34
+ private final List <Diff > diffs = new ArrayList <Diff >();
35
+ private final EnumSet <DiffFlags > flags ;
35
36
37
+ private JsonDiff (EnumSet <DiffFlags > flags ) {
38
+ this .flags = flags .clone ();
36
39
}
37
40
38
41
public static JsonNode asJson (final JsonNode source , final JsonNode target ) {
39
42
return asJson (source , target , DiffFlags .defaults ());
40
43
}
41
44
42
45
public static JsonNode asJson (final JsonNode source , final JsonNode target , EnumSet <DiffFlags > flags ) {
43
- final List <Diff > diffs = new ArrayList <Diff >();
44
- List <Object > path = new ArrayList <Object >(0 );
46
+ JsonDiff diff = new JsonDiff (flags );
45
47
46
48
// generating diffs in the order of their occurrence
49
+ List <Object > path = new ArrayList <Object >(0 );
50
+ diff .generateDiffs (path , source , target );
47
51
48
- generateDiffs (diffs , path , source , target );
49
-
50
- if (!flags .contains (DiffFlags .OMIT_MOVE_OPERATION )) {
51
-
52
+ if (!flags .contains (DiffFlags .OMIT_MOVE_OPERATION ))
52
53
// Merging remove & add to move operation
54
+ diff .introduceMoveOperation ();
53
55
54
- compactDiffs (diffs );
55
- }
56
-
57
- if (!flags .contains (DiffFlags .OMIT_COPY_OPERATION )) {
58
-
56
+ if (!flags .contains (DiffFlags .OMIT_COPY_OPERATION ))
59
57
// Introduce copy operation
58
+ diff .introduceCopyOperation (source , target );
60
59
61
- introduceCopyOperation (source , target , diffs );
62
- }
63
-
64
- return getJsonNodes (diffs , flags );
60
+ return diff .getJsonNodes ();
65
61
}
66
62
67
63
private static List <Object > getMatchingValuePath (Map <JsonNode , List <Object >> unchangedValues , JsonNode value ) {
68
64
return unchangedValues .get (value );
69
65
}
70
66
71
- private static void introduceCopyOperation (JsonNode source , JsonNode target , List < Diff > diffs ) {
67
+ private void introduceCopyOperation (JsonNode source , JsonNode target ) {
72
68
Map <JsonNode , List <Object >> unchangedValues = getUnchangedPart (source , target );
69
+
73
70
for (int i = 0 ; i < diffs .size (); i ++) {
74
71
Diff diff = diffs .get (i );
75
- if (Operation .ADD == diff .getOperation ()) {
76
- List <Object > matchingValuePath = getMatchingValuePath (unchangedValues , diff .getValue ());
77
- if (matchingValuePath != null && isAllowed (matchingValuePath , diff .getPath ())) {
78
- diffs .set (i , new Diff (Operation .COPY , matchingValuePath , diff .getPath ()));
72
+ if (Operation .ADD != diff .getOperation ()) continue ;
73
+
74
+ List <Object > matchingValuePath = getMatchingValuePath (unchangedValues , diff .getValue ());
75
+ if (matchingValuePath != null && isAllowed (matchingValuePath , diff .getPath ())) {
76
+ // Matching value found; replace add with copy
77
+ if (flags .contains (DiffFlags .EMIT_TEST_OPERATIONS )) {
78
+ // Prepend test node
79
+ diffs .add (i , new Diff (Operation .TEST , matchingValuePath , diff .getValue ()));
80
+ i ++;
79
81
}
82
+ diffs .set (i , new Diff (Operation .COPY , matchingValuePath , diff .getPath ()));
80
83
}
81
84
}
82
85
}
@@ -171,7 +174,7 @@ private static void computeObject(Map<JsonNode, List<Object>> unchangedValues, L
171
174
* This method merge 2 diffs ( remove then add, or vice versa ) with same value into one Move operation,
172
175
* all the core logic resides here only
173
176
*/
174
- private static void compactDiffs ( List < Diff > diffs ) {
177
+ private void introduceMoveOperation ( ) {
175
178
for (int i = 0 ; i < diffs .size (); i ++) {
176
179
Diff diff1 = diffs .get (i );
177
180
@@ -234,7 +237,7 @@ private static void updatePathWithCounters(List<Integer> counters, List<Object>
234
237
for (int i = 0 ; i < counters .size (); i ++) {
235
238
int value = counters .get (i );
236
239
if (value != 0 ) {
237
- Integer currValue = Integer .parseInt (path .get (i ).toString ());
240
+ int currValue = Integer .parseInt (path .get (i ).toString ());
238
241
path .set (i , String .valueOf (currValue + value ));
239
242
}
240
243
}
@@ -270,7 +273,7 @@ private static void updateCounters(Diff pseudo, int idx, List<Integer> counters)
270
273
}
271
274
}
272
275
273
- private static ArrayNode getJsonNodes (List < Diff > diffs , EnumSet < DiffFlags > flags ) {
276
+ private ArrayNode getJsonNodes () {
274
277
JsonNodeFactory FACTORY = JsonNodeFactory .instance ;
275
278
final ArrayNode patch = FACTORY .arrayNode ();
276
279
for (Diff diff : diffs ) {
@@ -315,26 +318,27 @@ private static ObjectNode getJsonNode(JsonNodeFactory FACTORY, Diff diff, EnumSe
315
318
return jsonNode ;
316
319
}
317
320
318
- private static void generateDiffs (List < Diff > diffs , List <Object > path , JsonNode source , JsonNode target ) {
321
+ private void generateDiffs (List <Object > path , JsonNode source , JsonNode target ) {
319
322
if (!source .equals (target )) {
320
323
final NodeType sourceType = NodeType .getNodeType (source );
321
324
final NodeType targetType = NodeType .getNodeType (target );
322
325
323
326
if (sourceType == NodeType .ARRAY && targetType == NodeType .ARRAY ) {
324
327
//both are arrays
325
- compareArray (diffs , path , source , target );
328
+ compareArray (path , source , target );
326
329
} else if (sourceType == NodeType .OBJECT && targetType == NodeType .OBJECT ) {
327
330
//both are json
328
- compareObjects (diffs , path , source , target );
331
+ compareObjects (path , source , target );
329
332
} else {
330
333
//can be replaced
331
-
334
+ if (flags .contains (DiffFlags .EMIT_TEST_OPERATIONS ))
335
+ diffs .add (new Diff (Operation .TEST , path , source ));
332
336
diffs .add (Diff .generateDiff (Operation .REPLACE , path , source , target ));
333
337
}
334
338
}
335
339
}
336
340
337
- private static void compareArray (List < Diff > diffs , List <Object > path , JsonNode source , JsonNode target ) {
341
+ private void compareArray (List <Object > path , JsonNode source , JsonNode target ) {
338
342
List <JsonNode > lcs = getLCS (source , target );
339
343
int srcIdx = 0 ;
340
344
int targetIdx = 0 ;
@@ -365,12 +369,14 @@ private static void compareArray(List<Diff> diffs, List<Object> path, JsonNode s
365
369
} else if (lcsNode .equals (targetNode )) { //targetNode node is same as lcs, but not src
366
370
//removal,
367
371
List <Object > currPath = getPath (path , pos );
372
+ if (flags .contains (DiffFlags .EMIT_TEST_OPERATIONS ))
373
+ diffs .add (new Diff (Operation .TEST , currPath , srcNode ));
368
374
diffs .add (Diff .generateDiff (Operation .REMOVE , currPath , srcNode ));
369
375
srcIdx ++;
370
376
} else {
371
377
List <Object > currPath = getPath (path , pos );
372
378
//both are unequal to lcs node
373
- generateDiffs (diffs , currPath , srcNode , targetNode );
379
+ generateDiffs (currPath , srcNode , targetNode );
374
380
srcIdx ++;
375
381
targetIdx ++;
376
382
pos ++;
@@ -382,26 +388,26 @@ private static void compareArray(List<Diff> diffs, List<Object> path, JsonNode s
382
388
JsonNode srcNode = source .get (srcIdx );
383
389
JsonNode targetNode = target .get (targetIdx );
384
390
List <Object > currPath = getPath (path , pos );
385
- generateDiffs (diffs , currPath , srcNode , targetNode );
391
+ generateDiffs (currPath , srcNode , targetNode );
386
392
srcIdx ++;
387
393
targetIdx ++;
388
394
pos ++;
389
395
}
390
- pos = addRemaining (diffs , path , target , pos , targetIdx , targetSize );
391
- removeRemaining (diffs , path , pos , srcIdx , srcSize , source );
396
+ pos = addRemaining (path , target , pos , targetIdx , targetSize );
397
+ removeRemaining (path , pos , srcIdx , srcSize , source );
392
398
}
393
399
394
- private static Integer removeRemaining (List <Diff > diffs , List <Object > path , int pos , int srcIdx , int srcSize , JsonNode source ) {
395
-
400
+ private void removeRemaining (List <Object > path , int pos , int srcIdx , int srcSize , JsonNode source ) {
396
401
while (srcIdx < srcSize ) {
397
402
List <Object > currPath = getPath (path , pos );
403
+ if (flags .contains (DiffFlags .EMIT_TEST_OPERATIONS ))
404
+ diffs .add (new Diff (Operation .TEST , currPath , source .get (srcIdx )));
398
405
diffs .add (Diff .generateDiff (Operation .REMOVE , currPath , source .get (srcIdx )));
399
406
srcIdx ++;
400
407
}
401
- return pos ;
402
408
}
403
409
404
- private static Integer addRemaining (List < Diff > diffs , List <Object > path , JsonNode target , int pos , int targetIdx , int targetSize ) {
410
+ private int addRemaining (List <Object > path , JsonNode target , int pos , int targetIdx , int targetSize ) {
405
411
while (targetIdx < targetSize ) {
406
412
JsonNode jsonNode = target .get (targetIdx );
407
413
List <Object > currPath = getPath (path , pos );
@@ -412,18 +418,20 @@ private static Integer addRemaining(List<Diff> diffs, List<Object> path, JsonNod
412
418
return pos ;
413
419
}
414
420
415
- private static void compareObjects (List < Diff > diffs , List <Object > path , JsonNode source , JsonNode target ) {
421
+ private void compareObjects (List <Object > path , JsonNode source , JsonNode target ) {
416
422
Iterator <String > keysFromSrc = source .fieldNames ();
417
423
while (keysFromSrc .hasNext ()) {
418
424
String key = keysFromSrc .next ();
419
425
if (!target .has (key )) {
420
426
//remove case
421
427
List <Object > currPath = getPath (path , key );
428
+ if (flags .contains (DiffFlags .EMIT_TEST_OPERATIONS ))
429
+ diffs .add (new Diff (Operation .TEST , currPath , source .get (key )));
422
430
diffs .add (Diff .generateDiff (Operation .REMOVE , currPath , source .get (key )));
423
431
continue ;
424
432
}
425
433
List <Object > currPath = getPath (path , key );
426
- generateDiffs (diffs , currPath , source .get (key ), target .get (key ));
434
+ generateDiffs (currPath , source .get (key ), target .get (key ));
427
435
}
428
436
Iterator <String > keysFromTarget = target .fieldNames ();
429
437
while (keysFromTarget .hasNext ()) {
0 commit comments