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 package com.linecorp.centraldogma.internal.jsonpatch;
36
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.function.Supplier;
41
42 import javax.annotation.Nullable;
43
44 import com.fasterxml.jackson.core.JsonPointer;
45 import com.fasterxml.jackson.databind.JsonNode;
46 import com.google.common.base.Equivalence;
47 import com.google.common.base.Predicate;
48
49
50 final class DiffProcessor {
51
52 private static final Equivalence<JsonNode> EQUIVALENCE = JsonNumEquals.getInstance();
53
54 private final List<JsonPatchOperation> diffs = new ArrayList<>();
55 private final ReplaceMode replaceMode;
56 private final Supplier<Map<JsonPointer, JsonNode>> unchangedValuesSupplier;
57
58 DiffProcessor(ReplaceMode replaceMode, final Supplier<Map<JsonPointer, JsonNode>> unchangedValuesSupplier) {
59 this.replaceMode = replaceMode;
60 this.unchangedValuesSupplier = new Supplier<Map<JsonPointer, JsonNode>>() {
61
62 @Nullable
63 private Map<JsonPointer, JsonNode> unchangedValues;
64
65 @Override
66 public Map<JsonPointer, JsonNode> get() {
67 if (unchangedValues == null) {
68 unchangedValues = unchangedValuesSupplier.get();
69 }
70 return unchangedValues;
71 }
72 };
73 }
74
75 void valueReplaced(final JsonPointer pointer, final JsonNode oldValue, final JsonNode newValue) {
76 switch (replaceMode) {
77 case RFC6902:
78 diffs.add(new ReplaceOperation(pointer, newValue));
79 break;
80 case SAFE:
81 diffs.add(new SafeReplaceOperation(pointer, oldValue, newValue));
82 break;
83 }
84 }
85
86 void valueRemoved(final JsonPointer pointer) {
87 diffs.add(new RemoveOperation(pointer));
88 }
89
90 void valueAdded(final JsonPointer pointer, final JsonNode value) {
91 final JsonPatchOperation op;
92 if (value.isContainerNode()) {
93
94 final JsonPointer ptr = findUnchangedValue(value);
95 op = ptr != null ? new CopyOperation(ptr, pointer)
96 : new AddOperation(pointer, value);
97 } else {
98 op = new AddOperation(pointer, value);
99 }
100
101 diffs.add(op);
102 }
103
104 JsonPatch getPatch() {
105 return new JsonPatch(diffs);
106 }
107
108 @Nullable
109 private JsonPointer findUnchangedValue(final JsonNode value) {
110 final Map<JsonPointer, JsonNode> unchangedValues = unchangedValuesSupplier.get();
111 if (unchangedValues.isEmpty()) {
112 return null;
113 }
114
115 final Predicate<JsonNode> predicate = EQUIVALENCE.equivalentTo(value);
116 for (final Map.Entry<JsonPointer, JsonNode> entry : unchangedValues.entrySet()) {
117 if (predicate.apply(entry.getValue())) {
118 return entry.getKey();
119 }
120 }
121 return null;
122 }
123 }