diff --git a/src/main/java/difflib/algorithm/DiffAlgorithm.java b/src/main/java/difflib/algorithm/DiffAlgorithm.java index 4f6059d..4fb44f1 100644 --- a/src/main/java/difflib/algorithm/DiffAlgorithm.java +++ b/src/main/java/difflib/algorithm/DiffAlgorithm.java @@ -38,7 +38,9 @@ public interface DiffAlgorithm { * @param revised The revised sequence. Must not be {@code null}. * @return The patch representing the diff of the given sequences. Never {@code null}. */ - public Patch diff(T[] original, T[] revised) throws DiffException; + public default Patch diff(T[] original, T[] revised) throws DiffException { + return diff(Arrays.asList(original), Arrays.asList(revised)); + } /** * Computes the difference between the original sequence and the revised sequence and returns it diff --git a/src/main/java/difflib/algorithm/myers/DiffNode.java b/src/main/java/difflib/algorithm/myers/DiffNode.java deleted file mode 100644 index 14fe1ba..0000000 --- a/src/main/java/difflib/algorithm/myers/DiffNode.java +++ /dev/null @@ -1,62 +0,0 @@ -/*- - * #%L - * java-diff-utils - * %% - * Copyright (C) 2009 - 2017 java-diff-utils - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - * #L% - */ -package difflib.algorithm.myers; - -/** - * A diffnode in a diffpath. - *

- * A DiffNode and its previous node mark a delta between two input sequences, that is, two differing - * subsequences between (possibly zero length) matching sequences. - * - * {@link DiffNode DiffNodes} and {@link Snake Snakes} allow for compression of diffpaths, as each - * snake is represented by a single {@link Snake Snake} node and each contiguous series of - * insertions and deletions is represented by a single {@link DiffNode DiffNodes}. - * - * @author Juanco Anez - * - */ -public final class DiffNode extends PathNode { - - /** - * Constructs a DiffNode. - *

- * DiffNodes are compressed. That means that the path pointed to by the prev - * parameter will be followed using {@link PathNode#previousSnake} until a non-diff node is - * found. - * - * @param the position in the original sequence - * @param the position in the revised sequence - * @param prev the previous node in the path. - */ - public DiffNode(int i, int j, PathNode prev) { - super(i, j, (prev == null ? null : prev.previousSnake())); - } - - /** - * {@inheritDoc} - * - * @return false, always - */ - @Override - public boolean isSnake() { - return false; - } - -} diff --git a/src/main/java/difflib/algorithm/myers/MyersDiff.java b/src/main/java/difflib/algorithm/myers/MyersDiff.java index 01d5e75..a4a39ec 100644 --- a/src/main/java/difflib/algorithm/myers/MyersDiff.java +++ b/src/main/java/difflib/algorithm/myers/MyersDiff.java @@ -74,16 +74,6 @@ public final class MyersDiff implements DiffAlgorithm { this.equalizer = equalizer; } - /** - * {@inheritDoc} - * - * @return Returns an empty diff if get the error while procession the difference. - */ - @Override - public Patch diff(final T[] original, final T[] revised) throws DiffException { - return diff(Arrays.asList(original), Arrays.asList(revised)); - } - /** * {@inheritDoc} * @@ -93,7 +83,7 @@ public final class MyersDiff implements DiffAlgorithm { public Patch diff(final List original, final List revised) throws DiffException { Objects.requireNonNull(original, "original list must not be null"); Objects.requireNonNull(revised, "revised list must not be null"); - + PathNode path = buildPath(original, revised); return buildRevision(path, original, revised); } @@ -121,7 +111,7 @@ public final class MyersDiff implements DiffAlgorithm { final int middle = size / 2; final PathNode diagonal[] = new PathNode[size]; - diagonal[middle + 1] = new Snake(0, -1, null); + diagonal[middle + 1] = new PathNode(0, -1, true, true, null); for (int d = 0; d < MAX; d++) { for (int k = -d; k <= d; k += 2) { final int kmiddle = middle + k; @@ -142,17 +132,15 @@ public final class MyersDiff implements DiffAlgorithm { int j = i - k; - PathNode node = new DiffNode(i, j, prev); + PathNode node = new PathNode(i, j, false, false, prev); - // orig and rev are zero-based - // but the algorithm is one-based - // that's why there's no +1 when indexing the sequences while (i < N && j < M && equalizer.equals(orig.get(i), rev.get(j))) { i++; j++; } - if (i > node.i) { - node = new Snake(i, j, node); + + if (i != node.i) { + node = new PathNode(i, j, true, false, node); } diagonal[kmiddle] = node; @@ -162,7 +150,6 @@ public final class MyersDiff implements DiffAlgorithm { } } diagonal[middle + d - 1] = null; - } // According to Myers, this cannot happen throw new DifferentiationFailedException("could not find a diff path"); diff --git a/src/main/java/difflib/algorithm/myers/PathNode.java b/src/main/java/difflib/algorithm/myers/PathNode.java index de1a2dd..c9490f4 100644 --- a/src/main/java/difflib/algorithm/myers/PathNode.java +++ b/src/main/java/difflib/algorithm/myers/PathNode.java @@ -22,13 +22,13 @@ package difflib.algorithm.myers; /** * A node in a diffpath. * - * @author Juanco Anez + * @author Juanco Anez * * @see DiffNode * @see Snake * */ -public abstract class PathNode { +public final class PathNode { /** * Position in the original sequence. @@ -43,6 +43,10 @@ public abstract class PathNode { */ public final PathNode prev; + public final boolean snake; + + public final boolean bootstrap; + /** * Concatenates a new path node with an existing diffpath. * @@ -50,18 +54,21 @@ public abstract class PathNode { * @param j The position in the revised sequence for the new node. * @param prev The previous node in the path. */ - public PathNode(int i, int j, PathNode prev) { + public PathNode(int i, int j, boolean snake, boolean bootstrap, PathNode prev) { this.i = i; this.j = j; - this.prev = prev; + this.bootstrap = bootstrap; + if (snake) { + this.prev = prev; + } else { + this.prev = (prev == null ? null : prev.previousSnake()); + } + this.snake = snake; } - /** - * Is this node a {@link Snake Snake node}? - * - * @return true if this is a {@link Snake Snake node} - */ - public abstract boolean isSnake(); + public boolean isSnake() { + return snake; + } /** * Is this a bootstrap node? @@ -71,7 +78,7 @@ public abstract class PathNode { * @return tru if this is a bootstrap node. */ public boolean isBootstrap() { - return i < 0 || j < 0; + return bootstrap; } /** diff --git a/src/main/java/difflib/algorithm/myers/Snake.java b/src/main/java/difflib/algorithm/myers/Snake.java deleted file mode 100644 index 1ecafdd..0000000 --- a/src/main/java/difflib/algorithm/myers/Snake.java +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * #%L - * java-diff-utils - * %% - * Copyright (C) 2009 - 2017 java-diff-utils - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - * #L% - */ -package difflib.algorithm.myers; - -/** - * Represents a snake in a diffpath. - *

- * - * {@link DiffNode DiffNodes} and {@link Snake Snakes} allow for compression of diffpaths, as each - * snake is represented by a single {@link Snake Snake} node and each contiguous series of - * insertions and deletions is represented by a single {@link DiffNode DiffNodes}. - * - * @author Juanco Anez - * - */ -public final class Snake extends PathNode { - - /** - * Constructs a snake node. - * - * @param the position in the original sequence - * @param the position in the revised sequence - * @param prev the previous node in the path. - */ - public Snake(int i, int j, PathNode prev) { - super(i, j, prev); - } - - /** - * {@inheritDoc} - * - * @return true always - */ - @Override - public boolean isSnake() { - return true; - } - -} diff --git a/src/test/java/difflib/DiffUtilsTest.java b/src/test/java/difflib/DiffUtilsTest.java index 680f56b..5c8ded4 100644 --- a/src/test/java/difflib/DiffUtilsTest.java +++ b/src/test/java/difflib/DiffUtilsTest.java @@ -145,11 +145,19 @@ public class DiffUtilsTest { assertEquals(1, patch.getDeltas().size()); } - private static List readStringListFromInputStream(InputStream is) throws IOException { + public static List readStringListFromInputStream(InputStream is) throws IOException { try (BufferedReader reader = new BufferedReader( new InputStreamReader(is, Charset.forName(StandardCharsets.UTF_8.name())))) { return reader.lines().collect(toList()); } } + + @Test + public void testDiffMyersExample1() throws DiffException { + final Patch patch = DiffUtils.diff(Arrays.asList("A","B","C","A","B","B","A"), Arrays.asList("C","B","A","B","A","C")); + assertNotNull(patch); + assertEquals(4, patch.getDeltas().size()); + assertEquals("Patch{deltas=[[DeleteDelta, position: 0, lines: [A, B]], [InsertDelta, position: 3, lines: [B]], [DeleteDelta, position: 5, lines: [B]], [InsertDelta, position: 7, lines: [C]]]}", patch.toString()); + } } diff --git a/src/test/java/difflib/algorithm/myers/MyersDiffTest.java b/src/test/java/difflib/algorithm/myers/MyersDiffTest.java new file mode 100644 index 0000000..44a9a28 --- /dev/null +++ b/src/test/java/difflib/algorithm/myers/MyersDiffTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2017 java-diff-utils. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package difflib.algorithm.myers; + +import difflib.DiffUtils; +import difflib.algorithm.DiffException; +import difflib.patch.Patch; +import java.util.Arrays; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author tw + */ +public class MyersDiffTest { + + public MyersDiffTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + + + @Test + public void testDiffMyersExample1Forward() throws DiffException { + final Patch patch = new MyersDiff().diff( + Arrays.asList("A","B","C","A","B","B","A"), + Arrays.asList("C","B","A","B","A","C")); + assertNotNull(patch); + assertEquals(4, patch.getDeltas().size()); + assertEquals("Patch{deltas=[[DeleteDelta, position: 0, lines: [A, B]], [InsertDelta, position: 3, lines: [B]], [DeleteDelta, position: 5, lines: [B]], [InsertDelta, position: 7, lines: [C]]]}", patch.toString()); + } + +}