@@ -66,6 +66,117 @@ public List<T> applyTo(List<T> target) throws PatchFailedException {
66
66
return result ;
67
67
}
68
68
69
+ private static class PatchApplyingContext <T > {
70
+ public final List <T > result ;
71
+ public final int maxFuzz ;
72
+
73
+ // the position last patch applied to.
74
+ public int lastPatchEnd = -1 ;
75
+
76
+ ///// passing values from find to apply
77
+ public int currentFuzz = 0 ;
78
+
79
+ public int defaultPosition ;
80
+ public boolean beforeOutRange = false ;
81
+ public boolean afterOutRange = false ;
82
+
83
+ private PatchApplyingContext (List <T > result , int maxFuzz ) {
84
+ this .result = result ;
85
+ this .maxFuzz = maxFuzz ;
86
+ }
87
+ }
88
+
89
+ public List <T > applyFuzzy (List <T > target , int maxFuzz ) throws PatchFailedException {
90
+ PatchApplyingContext <T > ctx = new PatchApplyingContext <>(new ArrayList <>(target ), maxFuzz );
91
+
92
+ // the difference between patch's position and actually applied position
93
+ int lastPatchDelta = 0 ;
94
+
95
+ for (AbstractDelta <T > delta : getDeltas ()) {
96
+ ctx .defaultPosition = delta .getSource ().getPosition () + lastPatchDelta ;
97
+ int patchPosition = findPositionFuzzy (ctx , delta );
98
+ if (0 <= patchPosition ) {
99
+ delta .applyFuzzyToAt (ctx .result , ctx .currentFuzz , patchPosition );
100
+ lastPatchDelta = patchPosition - delta .getSource ().getPosition ();
101
+ ctx .lastPatchEnd = delta .getSource ().last () + lastPatchDelta ;
102
+ } else {
103
+ conflictOutput .processConflict (VerifyChunk .CONTENT_DOES_NOT_MATCH_TARGET , delta , ctx .result );
104
+ }
105
+ }
106
+
107
+ return ctx .result ;
108
+ }
109
+
110
+ // negative for not found
111
+ private int findPositionFuzzy (PatchApplyingContext <T > ctx , AbstractDelta <T > delta ) throws PatchFailedException {
112
+ for (int fuzz = 0 ; fuzz <= ctx .maxFuzz ; fuzz ++) {
113
+ ctx .currentFuzz = fuzz ;
114
+ int foundPosition = findPositionWithFuzz (ctx , delta , fuzz );
115
+ if (foundPosition >= 0 ) {
116
+ return foundPosition ;
117
+ }
118
+ }
119
+ return -1 ;
120
+ }
121
+
122
+ // negative for not found
123
+ private int findPositionWithFuzz (PatchApplyingContext <T > ctx , AbstractDelta <T > delta , int fuzz ) throws PatchFailedException {
124
+ if (delta .getSource ().verifyChunk (ctx .result , fuzz , ctx .defaultPosition ) == VerifyChunk .OK ) {
125
+ return ctx .defaultPosition ;
126
+ }
127
+
128
+ ctx .beforeOutRange = false ;
129
+ ctx .afterOutRange = false ;
130
+
131
+ // moreDelta >= 0: just for overflow guard, not a normal condition
132
+ //noinspection OverflowingLoopIndex
133
+ for (int moreDelta = 0 ; moreDelta >= 0 ; moreDelta ++) {
134
+ int pos = findPositionWithFuzzAndMoreDelta (ctx , delta , fuzz , moreDelta );
135
+ if (pos >= 0 ) {
136
+ return pos ;
137
+ }
138
+ if (ctx .beforeOutRange && ctx .afterOutRange ) {
139
+ break ;
140
+ }
141
+ }
142
+
143
+ return -1 ;
144
+ }
145
+
146
+ // negative for not found
147
+ private int findPositionWithFuzzAndMoreDelta (PatchApplyingContext <T > ctx , AbstractDelta <T > delta , int fuzz , int moreDelta ) throws PatchFailedException {
148
+ // range check: can't apply before end of last patch
149
+ if (!ctx .beforeOutRange ) {
150
+ int beginAt = ctx .defaultPosition - moreDelta + fuzz ;
151
+ // We can't apply patch before end of last patch.
152
+ if (beginAt <= ctx .lastPatchEnd ) {
153
+ ctx .beforeOutRange = true ;
154
+ }
155
+ }
156
+ // range check: can't apply after end of result
157
+ if (!ctx .afterOutRange ) {
158
+ int beginAt = ctx .defaultPosition + moreDelta + delta .getSource ().size () - fuzz ;
159
+ // We can't apply patch before end of last patch.
160
+ if (ctx .result .size () < beginAt ) {
161
+ ctx .afterOutRange = true ;
162
+ }
163
+ }
164
+
165
+ if (!ctx .beforeOutRange ) {
166
+ VerifyChunk before = delta .getSource ().verifyChunk (ctx .result , fuzz , ctx .defaultPosition - moreDelta );
167
+ if (before == VerifyChunk .OK ) {
168
+ return ctx .defaultPosition - moreDelta ;
169
+ }
170
+ }
171
+ if (!ctx .afterOutRange ) {
172
+ VerifyChunk after = delta .getSource ().verifyChunk (ctx .result , fuzz , ctx .defaultPosition + moreDelta );
173
+ if (after == VerifyChunk .OK ) {
174
+ return ctx .defaultPosition + moreDelta ;
175
+ }
176
+ }
177
+ return -1 ;
178
+ }
179
+
69
180
/**
70
181
* Standard Patch behaviour to throw an exception for pathching conflicts.
71
182
*/
0 commit comments