From 585d49d00c27096abae2c970a085558f3cab4a0a Mon Sep 17 00:00:00 2001
From: Aeon Fruit
Date: Mon, 10 Feb 2020 23:44:34 +0100
Subject: [PATCH 1/3] Add word-wise comparator
---
.../apache/commons/text/diff/Comparator.java | 331 ++++++++++++++++++
.../commons/text/diff/StringsComparator.java | 300 +---------------
.../text/diff/WordWiseStringsComparator.java | 41 +++
.../diff/WordWiseStringsComparatorTest.java | 136 +++++++
4 files changed, 514 insertions(+), 294 deletions(-)
create mode 100755 src/main/java/org/apache/commons/text/diff/Comparator.java
mode change 100644 => 100755 src/main/java/org/apache/commons/text/diff/StringsComparator.java
create mode 100755 src/main/java/org/apache/commons/text/diff/WordWiseStringsComparator.java
create mode 100755 src/test/java/org/apache/commons/text/diff/WordWiseStringsComparatorTest.java
diff --git a/src/main/java/org/apache/commons/text/diff/Comparator.java b/src/main/java/org/apache/commons/text/diff/Comparator.java
new file mode 100755
index 0000000000..87889efed9
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/diff/Comparator.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.text.diff;
+
+/**
+ *
+ * It is guaranteed that the comparisons will always be done as
+ * {@code o1.equals(o2)} where {@code o1} belongs to the first
+ * sequence and {@code o2} belongs to the second sequence. This can
+ * be important if subclassing is used for some elements in the first
+ * sequence and the {@code equals} method is specialized.
+ *
+ *
+ * Comparison can be seen from two points of view: either as giving the smallest
+ * modification allowing to transform the first sequence into the second one, or
+ * as giving the longest sequence which is a subsequence of both initial
+ * sequences. The {@code equals} method is used to compare objects, so any
+ * object can be put into sequences. Modifications include deleting, inserting
+ * or keeping one object, starting from the beginning of the first sequence.
+ *
+ *
+ * This class implements the comparison algorithm, which is the very efficient
+ * algorithm from Eugene W. Myers
+ *
+ * An O(ND) Difference Algorithm and Its Variations. This algorithm produces
+ * the shortest possible {@link EditScript edit script} containing all the
+ * {@link EditCommand commands} needed to transform the first sequence into
+ * the second one.
+ *
+ *
+ * This code has been adapted from Apache Commons Collections 4.0.
+ *
+ *
+ * @see EditScript
+ * @see EditCommand
+ * @see CommandVisitor
+ * @since 1.0
+ */
+public class Comparator {
+
+ /**
+ * First sequence.
+ */
+ private final T[] left;
+ /**
+ * Second sequence.
+ */
+ private final T[] right;
+ /**
+ * Temporary array.
+ */
+ private final int[] vDown;
+ /**
+ * Temporary array.
+ */
+ private final int[] vUp;
+
+ /**
+ * Simple constructor.
+ *
+ * Creates a new instance of Comparator.
+ *
+ *
+ * It is guaranteed that the comparisons will always be done as
+ * {@code o1.equals(o2)} where {@code o1} belongs to the first
+ * sequence and {@code o2} belongs to the second sequence. This can be
+ * important if subclassing is used for some elements in the first sequence
+ * and the {@code equals} method is specialized.
+ *
+ *
+ * @param left first sequence to be compared
+ * @param right second sequence to be compared
+ */
+ public Comparator(final T[] left, final T[] right) {
+ this.left = left;
+ this.right = right;
+
+ final int size = left.length + right.length + 2;
+ vDown = new int[size];
+ vUp = new int[size];
+ }
+
+ /**
+ * Get the {@link EditScript} object.
+ *
+ * It is guaranteed that the objects embedded in the {@link InsertCommand
+ * insert commands} come from the second sequence and that the objects
+ * embedded in either the {@link DeleteCommand delete commands} or
+ * {@link KeepCommand keep commands} come from the first sequence. This can
+ * be important if subclassing is used for some elements in the first
+ * sequence and the {@code equals} method is specialized.
+ *
+ *
+ * @return The edit script resulting from the comparison of the two
+ * sequences
+ */
+ public EditScript getScript() {
+ final EditScript script = new EditScript<>();
+ buildScript(0, left.length, 0, right.length, script);
+ return script;
+ }
+
+ /**
+ * Build an edit script.
+ *
+ * @param start1 the begin of the first sequence to be compared
+ * @param end1 the end of the first sequence to be compared
+ * @param start2 the begin of the second sequence to be compared
+ * @param end2 the end of the second sequence to be compared
+ * @param script the edited script
+ */
+ private void buildScript(final int start1, final int end1, final int start2, final int end2,
+ final EditScript script) {
+ final Snake middle = getMiddleSnake(start1, end1, start2, end2);
+
+ if (middle == null
+ || middle.getStart() == end1 && middle.getDiag() == end1 - end2
+ || middle.getEnd() == start1 && middle.getDiag() == start1 - start2) {
+
+ int i = start1;
+ int j = start2;
+ while (i < end1 || j < end2) {
+ if (i < end1 && j < end2 && left[i].equals(right[j])) {
+ script.append(new KeepCommand<>(left[i]));
+ ++i;
+ ++j;
+ } else {
+ if (end1 - start1 > end2 - start2) {
+ script.append(new DeleteCommand<>(left[i]));
+ ++i;
+ } else {
+ script.append(new InsertCommand<>(right[j]));
+ ++j;
+ }
+ }
+ }
+
+ } else {
+
+ buildScript(start1, middle.getStart(),
+ start2, middle.getStart() - middle.getDiag(),
+ script);
+ for (int i = middle.getStart(); i < middle.getEnd(); ++i) {
+ script.append(new KeepCommand<>(left[i]));
+ }
+ buildScript(middle.getEnd(), end1,
+ middle.getEnd() - middle.getDiag(), end2,
+ script);
+ }
+ }
+
+ /**
+ * Get the middle snake corresponding to two subsequences of the
+ * main sequences.
+ *
+ * The snake is found using the MYERS Algorithm (this algorithms has
+ * also been implemented in the GNU diff program). This algorithm is
+ * explained in Eugene Myers article:
+ *
+ * An O(ND) Difference Algorithm and Its Variations.
+ *
+ *
+ * @param start1 the begin of the first sequence to be compared
+ * @param end1 the end of the first sequence to be compared
+ * @param start2 the begin of the second sequence to be compared
+ * @param end2 the end of the second sequence to be compared
+ * @return The middle snake
+ */
+ private Snake getMiddleSnake(final int start1, final int end1, final int start2, final int end2) {
+ // Myers Algorithm
+ // Initialisations
+ final int m = end1 - start1;
+ final int n = end2 - start2;
+ if (m == 0 || n == 0) {
+ return null;
+ }
+
+ final int delta = m - n;
+ final int sum = n + m;
+ final int offset = (sum % 2 == 0 ? sum : sum + 1) / 2;
+ vDown[1 + offset] = start1;
+ vUp[1 + offset] = end1 + 1;
+
+ for (int d = 0; d <= offset; ++d) {
+ // Down
+ for (int k = -d; k <= d; k += 2) {
+ // First step
+
+ final int i = k + offset;
+ if (k == -d || k != d && vDown[i - 1] < vDown[i + 1]) {
+ vDown[i] = vDown[i + 1];
+ } else {
+ vDown[i] = vDown[i - 1] + 1;
+ }
+
+ int x = vDown[i];
+ int y = x - start1 + start2 - k;
+
+ while (x < end1 && y < end2 && left[x].equals(right[y])) {
+ vDown[i] = ++x;
+ ++y;
+ }
+ // Second step
+ if (delta % 2 != 0 && delta - d <= k && k <= delta + d) {
+ if (vUp[i - delta] <= vDown[i]) { // NOPMD
+ return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2);
+ }
+ }
+ }
+
+ // Up
+ for (int k = delta - d; k <= delta + d; k += 2) {
+ // First step
+ final int i = k + offset - delta;
+ if (k == delta - d
+ || k != delta + d && vUp[i + 1] <= vUp[i - 1]) {
+ vUp[i] = vUp[i + 1] - 1;
+ } else {
+ vUp[i] = vUp[i - 1];
+ }
+
+ int x = vUp[i] - 1;
+ int y = x - start1 + start2 - k;
+ while (x >= start1 && y >= start2
+ && left[x].equals(right[y])) {
+ vUp[i] = x--;
+ y--;
+ }
+ // Second step
+ if (delta % 2 == 0 && -d <= k && k <= d) {
+ if (vUp[i] <= vDown[i + delta]) { // NOPMD
+ return buildSnake(vUp[i], k + start1 - start2, end1, end2);
+ }
+ }
+ }
+ }
+
+ // this should not happen
+ throw new RuntimeException("Internal Error");
+ }
+
+ /**
+ * Build a snake.
+ *
+ * @param start the value of the start of the snake
+ * @param diag the value of the diagonal of the snake
+ * @param end1 the value of the end of the first sequence to be compared
+ * @param end2 the value of the end of the second sequence to be compared
+ * @return The snake built
+ */
+ private Snake buildSnake(final int start, final int diag, final int end1, final int end2) {
+ int end = start;
+ while (end - diag < end2
+ && end < end1
+ && left[end].equals(right[end - diag])) {
+ ++end;
+ }
+ return new Snake(start, end, diag);
+ }
+
+ /**
+ * This class is a simple placeholder to hold the end part of a path
+ * under construction in a {@link Comparator Comparator}.
+ */
+ private static class Snake {
+
+ /** Start index. */
+ private final int start;
+
+ /** End index. */
+ private final int end;
+
+ /** Diagonal number. */
+ private final int diag;
+
+ /**
+ * Simple constructor. Creates a new instance of Snake with specified indices.
+ *
+ * @param start start index of the snake
+ * @param end end index of the snake
+ * @param diag diagonal number
+ */
+ Snake(final int start, final int end, final int diag) {
+ this.start = start;
+ this.end = end;
+ this.diag = diag;
+ }
+
+ /**
+ * Get the start index of the snake.
+ *
+ * @return start index of the snake
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Get the end index of the snake.
+ *
+ * @return end index of the snake
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Get the diagonal number of the snake.
+ *
+ * @return diagonal number of the snake
+ */
+ public int getDiag() {
+ return diag;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/text/diff/StringsComparator.java b/src/main/java/org/apache/commons/text/diff/StringsComparator.java
old mode 100644
new mode 100755
index b3963e5d5b..2ea62ff69b
--- a/src/main/java/org/apache/commons/text/diff/StringsComparator.java
+++ b/src/main/java/org/apache/commons/text/diff/StringsComparator.java
@@ -16,316 +16,28 @@
*/
package org.apache.commons.text.diff;
+import org.apache.commons.lang3.ArrayUtils;
+
/**
*
- * It is guaranteed that the comparisons will always be done as
- * {@code o1.equals(o2)} where {@code o1} belongs to the first
- * sequence and {@code o2} belongs to the second sequence. This can
- * be important if subclassing is used for some elements in the first
- * sequence and the {@code equals} method is specialized.
- *
- *
- * Comparison can be seen from two points of view: either as giving the smallest
- * modification allowing to transform the first sequence into the second one, or
- * as giving the longest sequence which is a subsequence of both initial
- * sequences. The {@code equals} method is used to compare objects, so any
- * object can be put into sequences. Modifications include deleting, inserting
- * or keeping one object, starting from the beginning of the first sequence.
+ * This class is a specialization of {@link Comparator class} adapted for sequences of {@code Character}.
*
- *
- * This class implements the comparison algorithm, which is the very efficient
- * algorithm from Eugene W. Myers
- *
- * An O(ND) Difference Algorithm and Its Variations. This algorithm produces
- * the shortest possible {@link EditScript edit script} containing all the
- * {@link EditCommand commands} needed to transform the first sequence into
- * the second one.
*
- *
- * This code has been adapted from Apache Commons Collections 4.0.
- *
- *
- * @see EditScript
- * @see EditCommand
- * @see CommandVisitor
- * @since 1.0
+ * @since 1.x
*/
-public class StringsComparator {
-
- /**
- * First character sequence.
- */
- private final String left;
- /**
- * Second character sequence.
- */
- private final String right;
- /**
- * Temporary array.
- */
- private final int[] vDown;
- /**
- * Temporary array.
- */
- private final int[] vUp;
+public class StringsComparator extends Comparator {
/**
* Simple constructor.
*
* Creates a new instance of StringsComparator.
*
- *
- * It is guaranteed that the comparisons will always be done as
- * {@code o1.equals(o2)} where {@code o1} belongs to the first
- * sequence and {@code o2} belongs to the second sequence. This can be
- * important if subclassing is used for some elements in the first sequence
- * and the {@code equals} method is specialized.
- *
*
* @param left first character sequence to be compared
* @param right second character sequence to be compared
*/
public StringsComparator(final String left, final String right) {
- this.left = left;
- this.right = right;
-
- final int size = left.length() + right.length() + 2;
- vDown = new int[size];
- vUp = new int[size];
- }
-
- /**
- * Get the {@link EditScript} object.
- *
- * It is guaranteed that the objects embedded in the {@link InsertCommand
- * insert commands} come from the second sequence and that the objects
- * embedded in either the {@link DeleteCommand delete commands} or
- * {@link KeepCommand keep commands} come from the first sequence. This can
- * be important if subclassing is used for some elements in the first
- * sequence and the {@code equals} method is specialized.
- *
- *
- * @return The edit script resulting from the comparison of the two
- * sequences
- */
- public EditScript getScript() {
- final EditScript script = new EditScript<>();
- buildScript(0, left.length(), 0, right.length(), script);
- return script;
- }
-
- /**
- * Build an edit script.
- *
- * @param start1 the begin of the first sequence to be compared
- * @param end1 the end of the first sequence to be compared
- * @param start2 the begin of the second sequence to be compared
- * @param end2 the end of the second sequence to be compared
- * @param script the edited script
- */
- private void buildScript(final int start1, final int end1, final int start2, final int end2,
- final EditScript script) {
- final Snake middle = getMiddleSnake(start1, end1, start2, end2);
-
- if (middle == null
- || middle.getStart() == end1 && middle.getDiag() == end1 - end2
- || middle.getEnd() == start1 && middle.getDiag() == start1 - start2) {
-
- int i = start1;
- int j = start2;
- while (i < end1 || j < end2) {
- if (i < end1 && j < end2 && left.charAt(i) == right.charAt(j)) {
- script.append(new KeepCommand<>(left.charAt(i)));
- ++i;
- ++j;
- } else {
- if (end1 - start1 > end2 - start2) {
- script.append(new DeleteCommand<>(left.charAt(i)));
- ++i;
- } else {
- script.append(new InsertCommand<>(right.charAt(j)));
- ++j;
- }
- }
- }
-
- } else {
-
- buildScript(start1, middle.getStart(),
- start2, middle.getStart() - middle.getDiag(),
- script);
- for (int i = middle.getStart(); i < middle.getEnd(); ++i) {
- script.append(new KeepCommand<>(left.charAt(i)));
- }
- buildScript(middle.getEnd(), end1,
- middle.getEnd() - middle.getDiag(), end2,
- script);
- }
- }
-
- /**
- * Get the middle snake corresponding to two subsequences of the
- * main sequences.
- *
- * The snake is found using the MYERS Algorithm (this algorithms has
- * also been implemented in the GNU diff program). This algorithm is
- * explained in Eugene Myers article:
- *
- * An O(ND) Difference Algorithm and Its Variations.
- *
- *
- * @param start1 the begin of the first sequence to be compared
- * @param end1 the end of the first sequence to be compared
- * @param start2 the begin of the second sequence to be compared
- * @param end2 the end of the second sequence to be compared
- * @return The middle snake
- */
- private Snake getMiddleSnake(final int start1, final int end1, final int start2, final int end2) {
- // Myers Algorithm
- // Initialisations
- final int m = end1 - start1;
- final int n = end2 - start2;
- if (m == 0 || n == 0) {
- return null;
- }
-
- final int delta = m - n;
- final int sum = n + m;
- final int offset = (sum % 2 == 0 ? sum : sum + 1) / 2;
- vDown[1 + offset] = start1;
- vUp[1 + offset] = end1 + 1;
-
- for (int d = 0; d <= offset; ++d) {
- // Down
- for (int k = -d; k <= d; k += 2) {
- // First step
-
- final int i = k + offset;
- if (k == -d || k != d && vDown[i - 1] < vDown[i + 1]) {
- vDown[i] = vDown[i + 1];
- } else {
- vDown[i] = vDown[i - 1] + 1;
- }
-
- int x = vDown[i];
- int y = x - start1 + start2 - k;
-
- while (x < end1 && y < end2 && left.charAt(x) == right.charAt(y)) {
- vDown[i] = ++x;
- ++y;
- }
- // Second step
- if (delta % 2 != 0 && delta - d <= k && k <= delta + d) {
- if (vUp[i - delta] <= vDown[i]) { // NOPMD
- return buildSnake(vUp[i - delta], k + start1 - start2, end1, end2);
- }
- }
- }
-
- // Up
- for (int k = delta - d; k <= delta + d; k += 2) {
- // First step
- final int i = k + offset - delta;
- if (k == delta - d
- || k != delta + d && vUp[i + 1] <= vUp[i - 1]) {
- vUp[i] = vUp[i + 1] - 1;
- } else {
- vUp[i] = vUp[i - 1];
- }
-
- int x = vUp[i] - 1;
- int y = x - start1 + start2 - k;
- while (x >= start1 && y >= start2
- && left.charAt(x) == right.charAt(y)) {
- vUp[i] = x--;
- y--;
- }
- // Second step
- if (delta % 2 == 0 && -d <= k && k <= d) {
- if (vUp[i] <= vDown[i + delta]) { // NOPMD
- return buildSnake(vUp[i], k + start1 - start2, end1, end2);
- }
- }
- }
- }
-
- // this should not happen
- throw new RuntimeException("Internal Error");
- }
-
- /**
- * Build a snake.
- *
- * @param start the value of the start of the snake
- * @param diag the value of the diagonal of the snake
- * @param end1 the value of the end of the first sequence to be compared
- * @param end2 the value of the end of the second sequence to be compared
- * @return The snake built
- */
- private Snake buildSnake(final int start, final int diag, final int end1, final int end2) {
- int end = start;
- while (end - diag < end2
- && end < end1
- && left.charAt(end) == right.charAt(end - diag)) {
- ++end;
- }
- return new Snake(start, end, diag);
- }
-
- /**
- * This class is a simple placeholder to hold the end part of a path
- * under construction in a {@link StringsComparator StringsComparator}.
- */
- private static class Snake {
-
- /** Start index. */
- private final int start;
-
- /** End index. */
- private final int end;
-
- /** Diagonal number. */
- private final int diag;
-
- /**
- * Simple constructor. Creates a new instance of Snake with specified indices.
- *
- * @param start start index of the snake
- * @param end end index of the snake
- * @param diag diagonal number
- */
- Snake(final int start, final int end, final int diag) {
- this.start = start;
- this.end = end;
- this.diag = diag;
- }
-
- /**
- * Get the start index of the snake.
- *
- * @return start index of the snake
- */
- public int getStart() {
- return start;
- }
-
- /**
- * Get the end index of the snake.
- *
- * @return end index of the snake
- */
- public int getEnd() {
- return end;
- }
-
- /**
- * Get the diagonal number of the snake.
- *
- * @return diagonal number of the snake
- */
- public int getDiag() {
- return diag;
- }
+ super(ArrayUtils.toObject(left.toCharArray()), ArrayUtils.toObject(right.toCharArray()));
}
}
diff --git a/src/main/java/org/apache/commons/text/diff/WordWiseStringsComparator.java b/src/main/java/org/apache/commons/text/diff/WordWiseStringsComparator.java
new file mode 100755
index 0000000000..9f8a5e4e02
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/diff/WordWiseStringsComparator.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.text.diff;
+
+/**
+ *
+ * This class is a specialization of {@link Comparator class} adapted for sequences of {@code String} objects.
+ *
+ *
+ * @since 1.x
+ */
+public class WordWiseStringsComparator extends Comparator {
+
+ /**
+ * Simple constructor.
+ *
+ * Creates a new instance of WordWiseStringsComparator.
+ *
+ *
+ * @param left first sequence of {@code String} objects to be compared
+ * @param right second sequence of {@code String} objects to be compared
+ */
+ public WordWiseStringsComparator(String[] left, String[] right) {
+ super(left, right);
+ }
+
+}
diff --git a/src/test/java/org/apache/commons/text/diff/WordWiseStringsComparatorTest.java b/src/test/java/org/apache/commons/text/diff/WordWiseStringsComparatorTest.java
new file mode 100755
index 0000000000..23a2d02c6f
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/diff/WordWiseStringsComparatorTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.commons.text.diff;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for the WordWiseStringsComparator.
+ */
+public class WordWiseStringsComparatorTest {
+ private List before;
+ private List after;
+ private int[] length;
+ private int[] lcs;
+
+ @Test
+ public void testLength() {
+ for (int i = 0; i < before.size(); ++i) {
+ final WordWiseStringsComparator comparator = new WordWiseStringsComparator(before.get(i), after.get(i));
+ assertThat(comparator.getScript().getModifications()).isEqualTo(length[i]);
+ }
+ }
+
+ @Test
+ public void testLongestCommonSubsequence() {
+ for (int i = 0; i < before.size(); ++i) {
+ final WordWiseStringsComparator comparator = new WordWiseStringsComparator(before.get(i), after.get(i));
+ assertThat(comparator.getScript().getLCSLength()).isEqualTo(lcs[i]);
+ }
+ }
+
+ @Test
+ public void testExecution() {
+ for (int i = 0; i < before.size(); ++i) {
+ final ExecutionVisitor ev = new ExecutionVisitor();
+ new WordWiseStringsComparator(before.get(i), after.get(i)).getScript().visit(ev);
+ assertThat(ev.getSequence()).isEqualTo(after.get(i));
+ }
+ }
+
+ private static class ExecutionVisitor implements CommandVisitor {
+
+ private final ArrayList v;
+
+ ExecutionVisitor() {
+ v = new ArrayList<>();
+ }
+
+ @Override
+ public void visitInsertCommand(final String object) {
+ v.add(object);
+ }
+
+ @Override
+ public void visitKeepCommand(final String object) {
+ v.add(object);
+ }
+
+ @Override
+ public void visitDeleteCommand(final String object) {
+ // noop
+ }
+
+ public String[] getSequence() {
+ return v.toArray(new String[0]);
+ }
+ }
+
+ @BeforeEach
+ public void setUp() {
+ before = Arrays.asList(
+ new String[]{"there", "is", "a", "bottle", "and", "a", "glass"},
+ new String[]{"nematode", "empty", "knowledge"},
+ new String[]{"empty", "bottle"},
+ new String[]{""},
+ new String[]{"different", "string"},
+ new String[]{"ABCABBA"},
+ new String[]{"glop", "glop"});
+ after = Arrays.asList(
+ new String[]{"there", "are", "noodles", "and", "chopsticks"},
+ new String[]{"empty", "bottle"},
+ new String[]{"the", "bottle", "is", "empty"},
+ new String[]{""},
+ new String[]{"stranger", "stuff"},
+ new String[]{"CBABAC"},
+ new String[]{"pas", "glop", "pas", "glop"});
+ length = new int[]{
+ 8,
+ 3,
+ 4,
+ 0,
+ 4,
+ 2,
+ 2
+ };
+ lcs = new int[]{
+ 2,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 2
+ };
+ }
+
+ @AfterEach
+ public void tearDown() {
+ before = null;
+ after = null;
+ length = null;
+ }
+
+}
From 6d1e6a611feee9a143969722e4b71243e58c5a9d Mon Sep 17 00:00:00 2001
From: Aeon Fruit
Date: Tue, 11 Feb 2020 00:05:29 +0100
Subject: [PATCH 2/3] Fix javadoc
---
src/main/java/org/apache/commons/text/diff/Comparator.java | 4 +++-
.../java/org/apache/commons/text/diff/StringsComparator.java | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/apache/commons/text/diff/Comparator.java b/src/main/java/org/apache/commons/text/diff/Comparator.java
index 87889efed9..d96aaa9414 100755
--- a/src/main/java/org/apache/commons/text/diff/Comparator.java
+++ b/src/main/java/org/apache/commons/text/diff/Comparator.java
@@ -45,10 +45,12 @@
* This code has been adapted from Apache Commons Collections 4.0.
*
*
+ * @param the type of the contents of the compared sequences.
+ *
* @see EditScript
* @see EditCommand
* @see CommandVisitor
- * @since 1.0
+ * @since 1.x
*/
public class Comparator {
diff --git a/src/main/java/org/apache/commons/text/diff/StringsComparator.java b/src/main/java/org/apache/commons/text/diff/StringsComparator.java
index 2ea62ff69b..a6217ed515 100755
--- a/src/main/java/org/apache/commons/text/diff/StringsComparator.java
+++ b/src/main/java/org/apache/commons/text/diff/StringsComparator.java
@@ -23,7 +23,7 @@
* This class is a specialization of {@link Comparator class} adapted for sequences of {@code Character}.
*
*
- * @since 1.x
+ * @since 1.0
*/
public class StringsComparator extends Comparator {
From fa209a97991cac92b7d0ce9bccdf28d95d5d37c5 Mon Sep 17 00:00:00 2001
From: Aeon Fruit
Date: Tue, 11 Feb 2020 00:12:31 +0100
Subject: [PATCH 3/3] Fix spotbugs errors
---
src/main/java/org/apache/commons/text/diff/Comparator.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/apache/commons/text/diff/Comparator.java b/src/main/java/org/apache/commons/text/diff/Comparator.java
index d96aaa9414..0d202d827b 100755
--- a/src/main/java/org/apache/commons/text/diff/Comparator.java
+++ b/src/main/java/org/apache/commons/text/diff/Comparator.java
@@ -16,6 +16,8 @@
*/
package org.apache.commons.text.diff;
+import java.util.Arrays;
+
/**
*
* It is guaranteed that the comparisons will always be done as
@@ -88,8 +90,8 @@ public class Comparator {
* @param right second sequence to be compared
*/
public Comparator(final T[] left, final T[] right) {
- this.left = left;
- this.right = right;
+ this.left = Arrays.copyOf(left, left.length);
+ this.right = Arrays.copyOf(right, right.length);
final int size = left.length + right.length + 2;
vDown = new int[size];