From a52a9519fe55af904b191e91884b10bbac8748b2 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 10 Jun 2021 19:30:49 +0900 Subject: [PATCH 01/16] add applyFuzzyTo and restoreFuzzy --- .../github/difflib/patch/AbstractDelta.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java index 7db3ac50..3936382e 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java @@ -70,6 +70,33 @@ protected VerifyChunk verifyAntApplyTo(List target) throws PatchFailedExcepti protected abstract void restore(List target); + /** + * Apply patch fuzzy. + * + * @param target the list this patch will be applied to + * @param maxFuzz max fuzz size + * @param delta the changed offset from expected previous patch to actually applied position. + * @param lastPatchAppliedTo the position of target which + * @return the delta, changed offset from expected previous patch to actually applied position. + */ + @SuppressWarnings("RedundantThrows") + protected int applyFuzzyTo(List target, int maxFuzz, int delta, int lastPatchAppliedTo) throws PatchFailedException { + throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not supports applying patch fuzzy"); + } + + /** + * Restore patch fuzzy. + * + * @param target the list this patch will be applied to + * @param maxFuzz max fuzz size + * @param delta the changed offset from expected previous patch to actually applied position. + * @param lastPatchAppliedTo the position of target which + * @return the delta, changed offset from expected previous patch to actually applied position. + */ + protected int restoreFuzzy(List target, int maxFuzz, int delta, int lastPatchAppliedTo) { + throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not supports restoring patch fuzzy"); + } + /** * Create a new delta of the actual instance with customized chunk data. */ From 8368f286769fbccef764083d87270cbaabd521cf Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 10 Jun 2021 19:31:06 +0900 Subject: [PATCH 02/16] implement applyFuzzyTo and restoreFuzzy for EqualDelta --- .../src/main/java/com/github/difflib/patch/EqualDelta.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java index e13cf52e..017d881a 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java @@ -35,6 +35,11 @@ protected void applyTo(List target) throws PatchFailedException { protected void restore(List target) { } + @Override + protected int applyFuzzyTo(List target, int maxFuzz, int delta, int lastPatchAppliedTo) { + return delta; + } + @Override public String toString() { return "[EqualDelta, position: " + getSource().getPosition() + ", lines: " From 56c6898e07de0d623f053fd0d2f42a70b8c700a9 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Thu, 10 Jun 2021 21:04:19 +0900 Subject: [PATCH 03/16] add verifyChunk with fuzzy and delta --- .../java/com/github/difflib/patch/Chunk.java | 23 +++++++++- .../com/github/difflib/patch/ChunkTest.java | 43 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java index 8ff19875..3d355f8e 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java @@ -95,10 +95,29 @@ public Chunk(int position, T[] lines) { * @throws com.github.difflib.patch.PatchFailedException */ public VerifyChunk verifyChunk(List target) throws PatchFailedException { - if (position > target.size() || last() > target.size()) { + return verifyChunk(target, 0, 0); + } + + /** + * Verifies that this chunk's saved text matches the corresponding text in + * the given sequence. + * + * @param target the sequence to verify against. + * @param fuzz the count of ignored prefix/suffix + * @param delta the position of target which + * @throws com.github.difflib.patch.PatchFailedException + */ + public VerifyChunk verifyChunk(List target, int fuzz, int delta) throws PatchFailedException { + //noinspection UnnecessaryLocalVariable + int startIndex = fuzz; + int lastIndex = size() - fuzz; + int position = this.position + delta; + int last = last() + delta; + + if (position + fuzz > target.size() || last - fuzz > target.size()) { return VerifyChunk.POSITION_OUT_OF_TARGET; } - for (int i = 0; i < size(); i++) { + for (int i = startIndex; i < lastIndex; i++) { if (!target.get(position + i).equals(lines.get(i))) { return VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET; } diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java new file mode 100644 index 00000000..bcd3b019 --- /dev/null +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java @@ -0,0 +1,43 @@ +package com.github.difflib.patch; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +class ChunkTest { + @Test + void verifyChunk() throws PatchFailedException { + Chunk chunk = new Chunk<>(7, toCharList("test")); + + // normal check + assertEquals(VerifyChunk.OK, + chunk.verifyChunk(toCharList("prefix test suffix"))); + assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, + chunk.verifyChunk(toCharList("prefix es suffix"), 0, 0)); + + // delta + assertEquals(VerifyChunk.OK, + chunk.verifyChunk(toCharList("short test suffix"), 0, -1)); + assertEquals(VerifyChunk.OK, + chunk.verifyChunk(toCharList("loonger test suffix"), 0, 1)); + assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, + chunk.verifyChunk(toCharList("prefix test suffix"), 0, -1)); + assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, + chunk.verifyChunk(toCharList("prefix test suffix"), 0, 1)); + + // fuzz + assertEquals(VerifyChunk.OK, + chunk.verifyChunk(toCharList("prefix test suffix"), 1, 0)); + assertEquals(VerifyChunk.OK, + chunk.verifyChunk(toCharList("prefix es suffix"), 1, 0)); + assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, + chunk.verifyChunk(toCharList("prefix suffix"), 1, 0)); + } + + private List toCharList(String str) { + return str.chars().mapToObj(x -> (char)x).collect(Collectors.toList()); + } +} From 045a3f6ee4b21cff89ad0bca83d985063f165752 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 12 Jun 2021 20:15:56 +0900 Subject: [PATCH 04/16] implement fuzzy apply and add tests --- .../github/difflib/patch/AbstractDelta.java | 21 +- .../com/github/difflib/patch/ChangeDelta.java | 13 + .../java/com/github/difflib/patch/Chunk.java | 9 +- .../com/github/difflib/patch/EqualDelta.java | 3 +- .../java/com/github/difflib/patch/Patch.java | 94 +++++++ .../com/github/difflib/patch/ChunkTest.java | 18 +- .../com/github/difflib/patch/PatchTest.java | 248 ++++++++++++++++++ 7 files changed, 372 insertions(+), 34 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java index 3936382e..cfca8d66 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java @@ -74,29 +74,14 @@ protected VerifyChunk verifyAntApplyTo(List target) throws PatchFailedExcepti * Apply patch fuzzy. * * @param target the list this patch will be applied to - * @param maxFuzz max fuzz size - * @param delta the changed offset from expected previous patch to actually applied position. - * @param lastPatchAppliedTo the position of target which - * @return the delta, changed offset from expected previous patch to actually applied position. + * @param fuzz the count of elements which ignores prefix and suffix + * @param position the position this patch will be applied to. ignores {@code source.getPosition()} */ @SuppressWarnings("RedundantThrows") - protected int applyFuzzyTo(List target, int maxFuzz, int delta, int lastPatchAppliedTo) throws PatchFailedException { + protected void applyFuzzyToAt(List target, int fuzz, int position) throws PatchFailedException { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not supports applying patch fuzzy"); } - /** - * Restore patch fuzzy. - * - * @param target the list this patch will be applied to - * @param maxFuzz max fuzz size - * @param delta the changed offset from expected previous patch to actually applied position. - * @param lastPatchAppliedTo the position of target which - * @return the delta, changed offset from expected previous patch to actually applied position. - */ - protected int restoreFuzzy(List target, int maxFuzz, int delta, int lastPatchAppliedTo) { - throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not supports restoring patch fuzzy"); - } - /** * Create a new delta of the actual instance with customized chunk data. */ diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java index ddb37665..376fd625 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/ChangeDelta.java @@ -66,6 +66,19 @@ protected void restore(List target) { } } + protected void applyFuzzyToAt(List target, int fuzz, int position) throws PatchFailedException { + int size = getSource().size(); + for (int i = fuzz; i < size - fuzz; i++) { + target.remove(position + fuzz); + } + + int i = fuzz; + for (T line : getTarget().getLines().subList(fuzz, getTarget().size() - fuzz)) { + target.add(position + i, line); + i++; + } + } + @Override public String toString() { return "[ChangeDelta, position: " + getSource().getPosition() + ", lines: " diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java index 3d355f8e..7e55ac0d 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java @@ -95,7 +95,7 @@ public Chunk(int position, T[] lines) { * @throws com.github.difflib.patch.PatchFailedException */ public VerifyChunk verifyChunk(List target) throws PatchFailedException { - return verifyChunk(target, 0, 0); + return verifyChunk(target, 0, getPosition()); } /** @@ -104,15 +104,14 @@ public VerifyChunk verifyChunk(List target) throws PatchFailedException { * * @param target the sequence to verify against. * @param fuzz the count of ignored prefix/suffix - * @param delta the position of target which + * @param position the position of target * @throws com.github.difflib.patch.PatchFailedException */ - public VerifyChunk verifyChunk(List target, int fuzz, int delta) throws PatchFailedException { + public VerifyChunk verifyChunk(List target, int fuzz, int position) throws PatchFailedException { //noinspection UnnecessaryLocalVariable int startIndex = fuzz; int lastIndex = size() - fuzz; - int position = this.position + delta; - int last = last() + delta; + int last = position + size() - 1; if (position + fuzz > target.size() || last - fuzz > target.size()) { return VerifyChunk.POSITION_OUT_OF_TARGET; diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java index 017d881a..ba265cc1 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java @@ -36,8 +36,7 @@ protected void restore(List target) { } @Override - protected int applyFuzzyTo(List target, int maxFuzz, int delta, int lastPatchAppliedTo) { - return delta; + protected void applyFuzzyToAt(List target, int fuzz, int delta) { } @Override diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index 5a952f6c..e04173b0 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -66,6 +66,100 @@ public List applyTo(List target) throws PatchFailedException { return result; } + private static class PatchApplyingContext { + final List result; + final int maxFuzz; + + // the difference between patch's position and actually applied position + int lastPatchDelta = 0; + // the position last patch applied to. + int lastPatchEnd = -1; + + ///// passing values from find to apply + int currentFuzz = 0; + + private PatchApplyingContext(List result, int maxFuzz) { + this.result = result; + this.maxFuzz = maxFuzz; + } + } + + public List applyFuzzy(List target, int maxFuzz) throws PatchFailedException { + PatchApplyingContext ctx = new PatchApplyingContext<>(new ArrayList<>(target), maxFuzz); + + ListIterator> it = getDeltas().listIterator(deltas.size()); + while (it.hasPrevious()) { + AbstractDelta delta = it.previous(); + + int patchPosition = findPositionFuzzy(ctx, delta); + if (0 <= patchPosition) { + delta.applyFuzzyToAt(ctx.result, ctx.currentFuzz, patchPosition); + } else { + conflictOutput.processConflict(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, delta, ctx.result); + } + } + + return ctx.result; + } + + // negative for not found + private int findPositionFuzzy(PatchApplyingContext ctx, AbstractDelta delta) throws PatchFailedException { + for (int fuzz = 0; fuzz <= ctx.maxFuzz; fuzz++) { + ctx.currentFuzz = fuzz; + int foundPosition = findPositionWithFuzz(ctx, delta, fuzz); + if (foundPosition >= 0) return foundPosition; + } + return -1; + } + + // negative for not found + private int findPositionWithFuzz(PatchApplyingContext ctx, AbstractDelta delta, int fuzz) throws PatchFailedException { + Chunk original = delta.getSource(); + + // calculated position + int defaultPosition = original.getPosition() + ctx.lastPatchDelta; + + if (original.verifyChunk(ctx.result, fuzz, defaultPosition) == VerifyChunk.OK) { + return defaultPosition; + } + + boolean beforeOutRange = false; + boolean afterOutRange = false; + + // moreDelta >= 0: just for overflow guard, not a normal condition + //noinspection OverflowingLoopIndex + for (int moreDelta = 0; moreDelta >= 0; moreDelta++) { + // range check: can't apply before end of last patch + if (!beforeOutRange) { + int beginAt = defaultPosition - moreDelta + fuzz; + // We can't apply patch before end of last patch. + if (beginAt <= ctx.lastPatchEnd) { + beforeOutRange = true; + } + } + // range check: can't apply after end of result + if (!afterOutRange) { + int beginAt = defaultPosition + moreDelta + original.size() - fuzz; + // We can't apply patch before end of last patch. + if (ctx.result.size() < beginAt) { + afterOutRange = true; + } + } + + if (!beforeOutRange) { + VerifyChunk before = original.verifyChunk(ctx.result, fuzz, defaultPosition - moreDelta); + if (before == VerifyChunk.OK) return defaultPosition - moreDelta; + } + if (!afterOutRange) { + VerifyChunk after = original.verifyChunk(ctx.result, fuzz, defaultPosition + moreDelta); + if (after == VerifyChunk.OK) return defaultPosition + moreDelta; + } + if (beforeOutRange && afterOutRange) break; + } + + return -1; + } + /** * Standard Patch behaviour to throw an exception for pathching conflicts. */ diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java index bcd3b019..187d1138 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java @@ -16,25 +16,25 @@ void verifyChunk() throws PatchFailedException { assertEquals(VerifyChunk.OK, chunk.verifyChunk(toCharList("prefix test suffix"))); assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, - chunk.verifyChunk(toCharList("prefix es suffix"), 0, 0)); + chunk.verifyChunk(toCharList("prefix es suffix"), 0, 7)); - // delta + // position assertEquals(VerifyChunk.OK, - chunk.verifyChunk(toCharList("short test suffix"), 0, -1)); + chunk.verifyChunk(toCharList("short test suffix"), 0, 6)); assertEquals(VerifyChunk.OK, - chunk.verifyChunk(toCharList("loonger test suffix"), 0, 1)); + chunk.verifyChunk(toCharList("loonger test suffix"), 0, 8)); assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, - chunk.verifyChunk(toCharList("prefix test suffix"), 0, -1)); + chunk.verifyChunk(toCharList("prefix test suffix"), 0, 6)); assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, - chunk.verifyChunk(toCharList("prefix test suffix"), 0, 1)); + chunk.verifyChunk(toCharList("prefix test suffix"), 0, 8)); // fuzz assertEquals(VerifyChunk.OK, - chunk.verifyChunk(toCharList("prefix test suffix"), 1, 0)); + chunk.verifyChunk(toCharList("prefix test suffix"), 1, 7)); assertEquals(VerifyChunk.OK, - chunk.verifyChunk(toCharList("prefix es suffix"), 1, 0)); + chunk.verifyChunk(toCharList("prefix es suffix"), 1, 7)); assertEquals(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, - chunk.verifyChunk(toCharList("prefix suffix"), 1, 0)); + chunk.verifyChunk(toCharList("prefix suffix"), 1, 7)); } private List toCharList(String str) { diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java index 91d514a3..63f80f5a 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java @@ -1,6 +1,7 @@ package com.github.difflib.patch; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; @@ -8,8 +9,13 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.junit.jupiter.api.Test; @@ -56,6 +62,69 @@ public void testPatch_Change() { } } + // region testPatch_fuzzyApply utils + + private List intRange(int count) { + return IntStream.range(0, count) + .mapToObj(Integer::toString) + .collect(Collectors.toList()); + } + + @SafeVarargs + private final List join(List... lists) { + return Arrays.stream(lists).flatMap(Collection::stream).collect(Collectors.toList()); + } + + private static class FuzzyApplyTestPair { + final List from; + final List to; + final int requiredFuzz; + + private FuzzyApplyTestPair(List from, List to, int requiredFuzz) { + this.from = from; + this.to = to; + this.requiredFuzz = requiredFuzz; + } + } + + // endregion + + @Test + public void testPatch_fuzzyApply() throws PatchFailedException { + Patch patch = new Patch<>(); + List deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"); + List deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"); + patch.addDelta(new ChangeDelta<>( + new Chunk<>(6, deltaFrom), + new Chunk<>(6, deltaTo))); + + //noinspection unchecked + List[] moves = new List[] { + intRange(6), // no patch move + intRange(3), // forward patch move + intRange(9), // backward patch move + }; + + for (FuzzyApplyTestPair pair : pairs) { + for (List move : moves) { + List from = join(move, pair.from); + List to = join(move, pair.to); + + for (int i = 0; i < pair.requiredFuzz; i++) { + int maxFuzz = i; + assertThrows(PatchFailedException.class, () -> + patch.applyFuzzy(from, maxFuzz), + () -> "fail for " + from + " -> " + to + " for fuzz " + maxFuzz + " required " + pair.requiredFuzz); + } + for (int i = pair.requiredFuzz; i < 4; i++) { + int maxFuzz = i; + assertEquals(to, patch.applyFuzzy(from, maxFuzz), + () -> "with " + maxFuzz); + } + } + } + } + @Test public void testPatch_Serializable() throws IOException, ClassNotFoundException { final List changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd"); @@ -100,4 +169,183 @@ public void testPatch_Change_withExceptionProcessor() { fail(e.getMessage()); } } + + static class FuzzyApplyTestDataGenerator { + private static String createList(List values) { + return values.stream() + .map(x -> '"' + x + '"') + .collect(Collectors.joining(", ", "Arrays.asList(", ")")); + } + + public static void main(String[] args) { + String[] deltaFrom = new String[] { "aaa", "bbb", "ccc", "ddd", "eee", "fff" }; + String[] deltaTo = new String[] { "aaa", "bbb", "cxc", "dxd", "eee", "fff" }; + + List pairs = new ArrayList<>(); + + // create test data. + // Brute-force search + String[] changedValue = new String[]{"axa", "bxb", "czc", "dzd", "exe", "fxf"}; + for (int i = 0; i < 1 << 6; i++) { + if ((i & 0b001100) != 0 && (i & 0b001100) != 0b001100) continue; + + String[] from = deltaFrom.clone(); + String[] to = deltaTo.clone(); + for (int j = 0; j < 6; j++) { + if ((i & (1 << j)) != 0) { + from[j] = changedValue[j]; + to[j] = changedValue[j]; + } + } + + int requiredFuzz; + if ((i & 0b001100) != 0) requiredFuzz = 3; + else if ((i & 0b010010) != 0) requiredFuzz = 2; + else if ((i & 0b100001) != 0) requiredFuzz = 1; + else requiredFuzz = 0; + + pairs.add(new FuzzyApplyTestPair(Arrays.asList(from), Arrays.asList(to), requiredFuzz)); + } + pairs.sort(Comparator.comparingInt(a -> a.requiredFuzz)); + System.out.println("FuzzyApplyTestPair[] pairs = new FuzzyApplyTestPair[] {"); + for (FuzzyApplyTestPair pair : pairs) { + System.out.println(" new FuzzyApplyTestPair("); + System.out.println(" " + createList(pair.from) + ","); + System.out.println(" " + createList(pair.to) + ","); + System.out.println(" " + pair.requiredFuzz + "),"); + } + System.out.println("};"); + } + } + + private static final FuzzyApplyTestPair[] pairs = new FuzzyApplyTestPair[] { + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"), + Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"), + 0), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "ccc", "ddd", "eee", "fff"), + Arrays.asList("axa", "bbb", "cxc", "dxd", "eee", "fff"), + 1), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fxf"), + Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fxf"), + 1), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "ccc", "ddd", "eee", "fxf"), + Arrays.asList("axa", "bbb", "cxc", "dxd", "eee", "fxf"), + 1), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "ccc", "ddd", "eee", "fff"), + Arrays.asList("aaa", "bxb", "cxc", "dxd", "eee", "fff"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "ccc", "ddd", "eee", "fff"), + Arrays.asList("axa", "bxb", "cxc", "dxd", "eee", "fff"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "ccc", "ddd", "exe", "fff"), + Arrays.asList("aaa", "bbb", "cxc", "dxd", "exe", "fff"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "ccc", "ddd", "exe", "fff"), + Arrays.asList("axa", "bbb", "cxc", "dxd", "exe", "fff"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "ccc", "ddd", "exe", "fff"), + Arrays.asList("aaa", "bxb", "cxc", "dxd", "exe", "fff"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "ccc", "ddd", "exe", "fff"), + Arrays.asList("axa", "bxb", "cxc", "dxd", "exe", "fff"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "ccc", "ddd", "eee", "fxf"), + Arrays.asList("aaa", "bxb", "cxc", "dxd", "eee", "fxf"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "ccc", "ddd", "eee", "fxf"), + Arrays.asList("axa", "bxb", "cxc", "dxd", "eee", "fxf"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "ccc", "ddd", "exe", "fxf"), + Arrays.asList("aaa", "bbb", "cxc", "dxd", "exe", "fxf"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "ccc", "ddd", "exe", "fxf"), + Arrays.asList("axa", "bbb", "cxc", "dxd", "exe", "fxf"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "ccc", "ddd", "exe", "fxf"), + Arrays.asList("aaa", "bxb", "cxc", "dxd", "exe", "fxf"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "ccc", "ddd", "exe", "fxf"), + Arrays.asList("axa", "bxb", "cxc", "dxd", "exe", "fxf"), + 2), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fff"), + Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fff"), + Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fff"), + Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fff"), + Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fff"), + Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fff"), + Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fff"), + Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fff"), + Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fff"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fxf"), + Arrays.asList("aaa", "bbb", "czc", "dzd", "eee", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fxf"), + Arrays.asList("axa", "bbb", "czc", "dzd", "eee", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fxf"), + Arrays.asList("aaa", "bxb", "czc", "dzd", "eee", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fxf"), + Arrays.asList("axa", "bxb", "czc", "dzd", "eee", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fxf"), + Arrays.asList("aaa", "bbb", "czc", "dzd", "exe", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fxf"), + Arrays.asList("axa", "bbb", "czc", "dzd", "exe", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fxf"), + Arrays.asList("aaa", "bxb", "czc", "dzd", "exe", "fxf"), + 3), + new FuzzyApplyTestPair( + Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fxf"), + Arrays.asList("axa", "bxb", "czc", "dzd", "exe", "fxf"), + 3), + }; } From 857278e690e1790e6b880b0ac10ee2862d21dfdd Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 12 Jun 2021 22:17:05 +0900 Subject: [PATCH 05/16] fix style --- .../java/com/github/difflib/patch/Patch.java | 16 ++++++++++---- .../com/github/difflib/patch/ChunkTest.java | 2 +- .../com/github/difflib/patch/PatchTest.java | 21 ++++++++++++------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index e04173b0..c40c9840 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -107,7 +107,9 @@ private int findPositionFuzzy(PatchApplyingContext ctx, AbstractDelta delt for (int fuzz = 0; fuzz <= ctx.maxFuzz; fuzz++) { ctx.currentFuzz = fuzz; int foundPosition = findPositionWithFuzz(ctx, delta, fuzz); - if (foundPosition >= 0) return foundPosition; + if (foundPosition >= 0) { + return foundPosition; + } } return -1; } @@ -148,13 +150,19 @@ private int findPositionWithFuzz(PatchApplyingContext ctx, AbstractDelta d if (!beforeOutRange) { VerifyChunk before = original.verifyChunk(ctx.result, fuzz, defaultPosition - moreDelta); - if (before == VerifyChunk.OK) return defaultPosition - moreDelta; + if (before == VerifyChunk.OK) { + return defaultPosition - moreDelta; + } } if (!afterOutRange) { VerifyChunk after = original.verifyChunk(ctx.result, fuzz, defaultPosition + moreDelta); - if (after == VerifyChunk.OK) return defaultPosition + moreDelta; + if (after == VerifyChunk.OK) { + return defaultPosition + moreDelta; + } + } + if (beforeOutRange && afterOutRange) { + break; } - if (beforeOutRange && afterOutRange) break; } return -1; diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java index 187d1138..f9e6ed6f 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java @@ -38,6 +38,6 @@ void verifyChunk() throws PatchFailedException { } private List toCharList(String str) { - return str.chars().mapToObj(x -> (char)x).collect(Collectors.toList()); + return str.chars().mapToObj(x -> (char) x).collect(Collectors.toList()); } } diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java index 63f80f5a..fd609104 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java @@ -105,7 +105,7 @@ public void testPatch_fuzzyApply() throws PatchFailedException { intRange(9), // backward patch move }; - for (FuzzyApplyTestPair pair : pairs) { + for (FuzzyApplyTestPair pair : FUZZY_APPLY_TEST_PAIRS) { for (List move : moves) { List from = join(move, pair.from); List to = join(move, pair.to); @@ -187,7 +187,9 @@ public static void main(String[] args) { // Brute-force search String[] changedValue = new String[]{"axa", "bxb", "czc", "dzd", "exe", "fxf"}; for (int i = 0; i < 1 << 6; i++) { - if ((i & 0b001100) != 0 && (i & 0b001100) != 0b001100) continue; + if ((i & 0b001100) != 0 && (i & 0b001100) != 0b001100) { + continue; + } String[] from = deltaFrom.clone(); String[] to = deltaTo.clone(); @@ -199,10 +201,15 @@ public static void main(String[] args) { } int requiredFuzz; - if ((i & 0b001100) != 0) requiredFuzz = 3; - else if ((i & 0b010010) != 0) requiredFuzz = 2; - else if ((i & 0b100001) != 0) requiredFuzz = 1; - else requiredFuzz = 0; + if ((i & 0b001100) != 0) { + requiredFuzz = 3; + } else if ((i & 0b010010) != 0) { + requiredFuzz = 2; + } else if ((i & 0b100001) != 0) { + requiredFuzz = 1; + } else { + requiredFuzz = 0; + } pairs.add(new FuzzyApplyTestPair(Arrays.asList(from), Arrays.asList(to), requiredFuzz)); } @@ -218,7 +225,7 @@ public static void main(String[] args) { } } - private static final FuzzyApplyTestPair[] pairs = new FuzzyApplyTestPair[] { + private static final FuzzyApplyTestPair[] FUZZY_APPLY_TEST_PAIRS = new FuzzyApplyTestPair[] { new FuzzyApplyTestPair( Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"), Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"), From b135dc20ce74d5a4337771f9d7c5c0d2ab9dc97e Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 08:47:11 +0900 Subject: [PATCH 06/16] extract method --- .../java/com/github/difflib/patch/Patch.java | 81 ++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index c40c9840..84a02c44 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -78,6 +78,10 @@ private static class PatchApplyingContext { ///// passing values from find to apply int currentFuzz = 0; + int defaultPosition; + boolean beforeOutRange = false; + boolean afterOutRange = false; + private PatchApplyingContext(List result, int maxFuzz) { this.result = result; this.maxFuzz = maxFuzz; @@ -91,6 +95,7 @@ public List applyFuzzy(List target, int maxFuzz) throws PatchFailedExcepti while (it.hasPrevious()) { AbstractDelta delta = it.previous(); + ctx.defaultPosition = delta.getSource().getPosition() + ctx.lastPatchDelta; int patchPosition = findPositionFuzzy(ctx, delta); if (0 <= patchPosition) { delta.applyFuzzyToAt(ctx.result, ctx.currentFuzz, patchPosition); @@ -116,55 +121,59 @@ private int findPositionFuzzy(PatchApplyingContext ctx, AbstractDelta delt // negative for not found private int findPositionWithFuzz(PatchApplyingContext ctx, AbstractDelta delta, int fuzz) throws PatchFailedException { - Chunk original = delta.getSource(); - - // calculated position - int defaultPosition = original.getPosition() + ctx.lastPatchDelta; - - if (original.verifyChunk(ctx.result, fuzz, defaultPosition) == VerifyChunk.OK) { - return defaultPosition; + if (delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition) == VerifyChunk.OK) { + return ctx.defaultPosition; } - boolean beforeOutRange = false; - boolean afterOutRange = false; + ctx.beforeOutRange = false; + ctx.afterOutRange = false; // moreDelta >= 0: just for overflow guard, not a normal condition //noinspection OverflowingLoopIndex for (int moreDelta = 0; moreDelta >= 0; moreDelta++) { - // range check: can't apply before end of last patch - if (!beforeOutRange) { - int beginAt = defaultPosition - moreDelta + fuzz; - // We can't apply patch before end of last patch. - if (beginAt <= ctx.lastPatchEnd) { - beforeOutRange = true; - } + int pos = findPositionWithFuzzAndMoreDelta(ctx, delta, fuzz, moreDelta); + if (pos >= 0) { + return pos; } - // range check: can't apply after end of result - if (!afterOutRange) { - int beginAt = defaultPosition + moreDelta + original.size() - fuzz; - // We can't apply patch before end of last patch. - if (ctx.result.size() < beginAt) { - afterOutRange = true; - } + if (ctx.beforeOutRange && ctx.afterOutRange) { + break; } + } - if (!beforeOutRange) { - VerifyChunk before = original.verifyChunk(ctx.result, fuzz, defaultPosition - moreDelta); - if (before == VerifyChunk.OK) { - return defaultPosition - moreDelta; - } - } - if (!afterOutRange) { - VerifyChunk after = original.verifyChunk(ctx.result, fuzz, defaultPosition + moreDelta); - if (after == VerifyChunk.OK) { - return defaultPosition + moreDelta; - } + return -1; + } + + // negative for not found + private int findPositionWithFuzzAndMoreDelta(PatchApplyingContext ctx, AbstractDelta delta, int fuzz, int moreDelta) throws PatchFailedException { + // range check: can't apply before end of last patch + if (!ctx.beforeOutRange) { + int beginAt = ctx.defaultPosition - moreDelta + fuzz; + // We can't apply patch before end of last patch. + if (beginAt <= ctx.lastPatchEnd) { + ctx.beforeOutRange = true; } - if (beforeOutRange && afterOutRange) { - break; + } + // range check: can't apply after end of result + if (!ctx.afterOutRange) { + int beginAt = ctx.defaultPosition + moreDelta + delta.getSource().size() - fuzz; + // We can't apply patch before end of last patch. + if (ctx.result.size() < beginAt) { + ctx.afterOutRange = true; } } + if (!ctx.beforeOutRange) { + VerifyChunk before = delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition - moreDelta); + if (before == VerifyChunk.OK) { + return ctx.defaultPosition - moreDelta; + } + } + if (!ctx.afterOutRange) { + VerifyChunk after = delta.getSource().verifyChunk(ctx.result, fuzz, ctx.defaultPosition + moreDelta); + if (after == VerifyChunk.OK) { + return ctx.defaultPosition + moreDelta; + } + } return -1; } From 65a9e15aa4473781958d2602fe3402ba82c95093 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 08:53:04 +0900 Subject: [PATCH 07/16] fix: assign lastPatchDelta and lastPatchEnd --- .../src/main/java/com/github/difflib/patch/Patch.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index 84a02c44..ce600d72 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -70,8 +70,6 @@ private static class PatchApplyingContext { final List result; final int maxFuzz; - // the difference between patch's position and actually applied position - int lastPatchDelta = 0; // the position last patch applied to. int lastPatchEnd = -1; @@ -91,14 +89,19 @@ private PatchApplyingContext(List result, int maxFuzz) { public List applyFuzzy(List target, int maxFuzz) throws PatchFailedException { PatchApplyingContext ctx = new PatchApplyingContext<>(new ArrayList<>(target), maxFuzz); + // the difference between patch's position and actually applied position + int lastPatchDelta = 0; + ListIterator> it = getDeltas().listIterator(deltas.size()); while (it.hasPrevious()) { AbstractDelta delta = it.previous(); - ctx.defaultPosition = delta.getSource().getPosition() + ctx.lastPatchDelta; + ctx.defaultPosition = delta.getSource().getPosition() + lastPatchDelta; int patchPosition = findPositionFuzzy(ctx, delta); if (0 <= patchPosition) { delta.applyFuzzyToAt(ctx.result, ctx.currentFuzz, patchPosition); + lastPatchDelta = patchPosition - delta.getSource().getPosition(); + ctx.lastPatchEnd = delta.getSource().last() + lastPatchDelta; } else { conflictOutput.processConflict(VerifyChunk.CONTENT_DOES_NOT_MATCH_TARGET, delta, ctx.result); } From c1c0a98474715b5f0f50558605e3d2e05caa4d19 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 08:53:18 +0900 Subject: [PATCH 08/16] fix use explict import --- .../src/test/java/com/github/difflib/patch/ChunkTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java index f9e6ed6f..4816f221 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/ChunkTest.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; class ChunkTest { @Test From bea97f27527ad1bea3f7bfb2e942be8468e74431 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 08:59:47 +0900 Subject: [PATCH 09/16] document EqualDelta#applyFuzzyToAt --- .../src/main/java/com/github/difflib/patch/EqualDelta.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java index ba265cc1..5d23e3a4 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java @@ -35,6 +35,9 @@ protected void applyTo(List target) throws PatchFailedException { protected void restore(List target) { } + /** + * {@inheritDoc} + */ @Override protected void applyFuzzyToAt(List target, int fuzz, int delta) { } From 6ea0e4d47cb5f230f6aec3ccbe342e666871bc66 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 09:00:48 +0900 Subject: [PATCH 10/16] set access modifiers --- .../main/java/com/github/difflib/patch/Patch.java | 14 +++++++------- .../java/com/github/difflib/patch/PatchTest.java | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index ce600d72..a35b6992 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -67,18 +67,18 @@ public List applyTo(List target) throws PatchFailedException { } private static class PatchApplyingContext { - final List result; - final int maxFuzz; + public final List result; + public final int maxFuzz; // the position last patch applied to. - int lastPatchEnd = -1; + public int lastPatchEnd = -1; ///// passing values from find to apply - int currentFuzz = 0; + public int currentFuzz = 0; - int defaultPosition; - boolean beforeOutRange = false; - boolean afterOutRange = false; + public int defaultPosition; + public boolean beforeOutRange = false; + public boolean afterOutRange = false; private PatchApplyingContext(List result, int maxFuzz) { this.result = result; diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java index fd609104..c6629316 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java @@ -76,9 +76,9 @@ private final List join(List... lists) { } private static class FuzzyApplyTestPair { - final List from; - final List to; - final int requiredFuzz; + public final List from; + public final List to; + public final int requiredFuzz; private FuzzyApplyTestPair(List from, List to, int requiredFuzz) { this.from = from; From 04877355db08c5d36f9b81ea2d35b2cc422056d5 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 09:01:17 +0900 Subject: [PATCH 11/16] change test method name --- .../src/test/java/com/github/difflib/patch/PatchTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java index c6629316..b26a6631 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java @@ -90,7 +90,7 @@ private FuzzyApplyTestPair(List from, List to, int requiredFuzz) // endregion @Test - public void testPatch_fuzzyApply() throws PatchFailedException { + public void fuzzyApply() throws PatchFailedException { Patch patch = new Patch<>(); List deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"); List deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"); From 7ea7f97398303b8a8fce5fd61c402acd618d18f9 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 09:05:48 +0900 Subject: [PATCH 12/16] document empty method --- .../src/main/java/com/github/difflib/patch/EqualDelta.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java index 5d23e3a4..17fdadc6 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/EqualDelta.java @@ -40,6 +40,7 @@ protected void restore(List target) { */ @Override protected void applyFuzzyToAt(List target, int fuzz, int delta) { + // equals so no operations } @Override From 4a8fa6e836b444ff253ea0e97acf2b5963328260 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 09:15:49 +0900 Subject: [PATCH 13/16] apply from first --- .../src/main/java/com/github/difflib/patch/Patch.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index a35b6992..5e3e51f8 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -92,10 +92,7 @@ public List applyFuzzy(List target, int maxFuzz) throws PatchFailedExcepti // the difference between patch's position and actually applied position int lastPatchDelta = 0; - ListIterator> it = getDeltas().listIterator(deltas.size()); - while (it.hasPrevious()) { - AbstractDelta delta = it.previous(); - + for (AbstractDelta delta : getDeltas()) { ctx.defaultPosition = delta.getSource().getPosition() + lastPatchDelta; int patchPosition = findPositionFuzzy(ctx, delta); if (0 <= patchPosition) { From f82262f41da40b4e73889db3cfb839bf67d8f5a2 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 13 Jun 2021 09:18:19 +0900 Subject: [PATCH 14/16] add more tests --- .../com/github/difflib/patch/PatchTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java index b26a6631..69b72357 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/patch/PatchTest.java @@ -103,6 +103,7 @@ public void fuzzyApply() throws PatchFailedException { intRange(6), // no patch move intRange(3), // forward patch move intRange(9), // backward patch move + intRange(0), // apply to the first }; for (FuzzyApplyTestPair pair : FUZZY_APPLY_TEST_PAIRS) { @@ -125,6 +126,40 @@ public void fuzzyApply() throws PatchFailedException { } } + @Test + public void fuzzyApplyTwoSideBySidePatches() throws PatchFailedException { + Patch patch = new Patch<>(); + List deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"); + List deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"); + patch.addDelta(new ChangeDelta<>( + new Chunk<>(0, deltaFrom), + new Chunk<>(0, deltaTo))); + patch.addDelta(new ChangeDelta<>( + new Chunk<>(6, deltaFrom), + new Chunk<>(6, deltaTo))); + + + assertEquals(join(deltaTo, deltaTo), patch.applyFuzzy(join(deltaFrom, deltaFrom), 0)); + } + + @Test + public void fuzzyApplyToNearest() throws PatchFailedException { + Patch patch = new Patch<>(); + List deltaFrom = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"); + List deltaTo = Arrays.asList("aaa", "bbb", "cxc", "dxd", "eee", "fff"); + patch.addDelta(new ChangeDelta<>( + new Chunk<>(0, deltaFrom), + new Chunk<>(0, deltaTo))); + patch.addDelta(new ChangeDelta<>( + new Chunk<>(10, deltaFrom), + new Chunk<>(10, deltaTo))); + + assertEquals(join(deltaTo, deltaFrom, deltaTo), + patch.applyFuzzy(join(deltaFrom, deltaFrom, deltaFrom), 0)); + assertEquals(join(intRange(1), deltaTo, deltaFrom, deltaTo), + patch.applyFuzzy(join(intRange(1), deltaFrom, deltaFrom, deltaFrom), 0)); + } + @Test public void testPatch_Serializable() throws IOException, ClassNotFoundException { final List changeTest_from = Arrays.asList("aaa", "bbb", "ccc", "ddd"); From fba150946425dce19b010097f9cd483854b5ac0f Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 23 Nov 2021 16:41:55 +0900 Subject: [PATCH 15/16] add more documentation about fuzz parameter --- .../main/java/com/github/difflib/patch/AbstractDelta.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java index cfca8d66..65176bcd 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java @@ -74,7 +74,10 @@ protected VerifyChunk verifyAntApplyTo(List target) throws PatchFailedExcepti * Apply patch fuzzy. * * @param target the list this patch will be applied to - * @param fuzz the count of elements which ignores prefix and suffix + * @param fuzz the count of first/end elements which is not matched. + * If this value is two, this function should skip applying first and last two elements of this patch. + * If this is patch from 'ABCDEFG' to 'ABCHEFG' , this value is 2, the position is zero, + * and target is 'abCDEFg', after invocation of this method, the target should be 'abCHEFg'. * @param position the position this patch will be applied to. ignores {@code source.getPosition()} */ @SuppressWarnings("RedundantThrows") From 2f10732399b2e7eb27470bb3ef4a34cab42e8114 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 24 Nov 2021 11:05:52 +0900 Subject: [PATCH 16/16] fix documentation about fizzy patch Co-authored-by: cowwoc --- .../main/java/com/github/difflib/patch/AbstractDelta.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java index 65176bcd..a315e010 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/AbstractDelta.java @@ -74,11 +74,9 @@ protected VerifyChunk verifyAntApplyTo(List target) throws PatchFailedExcepti * Apply patch fuzzy. * * @param target the list this patch will be applied to - * @param fuzz the count of first/end elements which is not matched. - * If this value is two, this function should skip applying first and last two elements of this patch. - * If this is patch from 'ABCDEFG' to 'ABCHEFG' , this value is 2, the position is zero, - * and target is 'abCDEFg', after invocation of this method, the target should be 'abCHEFg'. + * @param fuzz the number of elements to ignore before/after the patched elements * @param position the position this patch will be applied to. ignores {@code source.getPosition()} + * @see Description of Fuzzy Patch for more information. */ @SuppressWarnings("RedundantThrows") protected void applyFuzzyToAt(List target, int fuzz, int position) throws PatchFailedException {