Skip to content

Commit 5502348

Browse files
committed
Merge branch 'dev'
# Conflicts: # version.txt
2 parents 8eddfc4 + 2b569a2 commit 5502348

28 files changed

Lines changed: 757 additions & 72 deletions

src/main/java/org/pageseeder/diffx/action/ActionsBuffer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424

2525
/**
2626
* Generates a list of actions from the output of the algorithms.
27-
* <p>
28-
* This handler is useful to capture the operations resulting from a diff and generate and edit script.
27+
*
28+
* <p>This handler is useful to capture the operations resulting from a diff
29+
* and generate and edit script.
2930
*
3031
* @author Christophe Lauret
3132
*
@@ -49,6 +50,7 @@ public class ActionsBuffer<T> implements DiffHandler<T> {
4950
@Override
5051
public void handle(Operator operator, T token) {
5152
setupAction(operator);
53+
assert this.action != null; // setupAction ensures it
5254
this.action.add(token);
5355
}
5456

src/main/java/org/pageseeder/diffx/algorithm/ElementStackFilter.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.jspecify.annotations.Nullable;
1919
import org.pageseeder.diffx.action.Operation;
2020
import org.pageseeder.diffx.api.DiffHandler;
21+
import org.pageseeder.diffx.api.Equality;
2122
import org.pageseeder.diffx.api.Operator;
2223
import org.pageseeder.diffx.handler.DiffFilter;
2324
import org.pageseeder.diffx.token.AttributeToken;
@@ -53,6 +54,20 @@ public final class ElementStackFilter extends DiffFilter<XMLToken> {
5354
*/
5455
private final Deque<Operation<StartElementToken>> elements;
5556

57+
private final Equality<XMLToken> eq;
58+
59+
/**
60+
* Constructs a new ElementStackFilter with a specified target handler.
61+
*
62+
* @param target The target handler that will be used to process XML tokens.
63+
* @param eq The strategy to compare elements for equality.
64+
*/
65+
public ElementStackFilter(DiffHandler<XMLToken> target, Equality<XMLToken> eq) {
66+
super(target);
67+
this.elements = new ArrayDeque<>(16);
68+
this.eq = eq;
69+
}
70+
5671
/**
5772
* Constructs a new ElementStackFilter with a specified target handler.
5873
*
@@ -61,6 +76,7 @@ public final class ElementStackFilter extends DiffFilter<XMLToken> {
6176
public ElementStackFilter(DiffHandler<XMLToken> target) {
6277
super(target);
6378
this.elements = new ArrayDeque<>(16);
79+
this.eq = XMLToken::equals;
6480
}
6581

6682
/**
@@ -145,7 +161,7 @@ public boolean isAllowed(Operator operator, XMLToken token) {
145161
public boolean matchCurrent(Operator operator, StartElementToken start) {
146162
Operation<StartElementToken> current = this.current();
147163
if (current == null) return false;
148-
return operator == current.operator() && start.equals(current.token());
164+
return operator == current.operator() && this.eq.equals(start, current.token());
149165
}
150166

151167
/**

src/main/java/org/pageseeder/diffx/algorithm/HirschbergAlgorithm.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.pageseeder.diffx.api.DiffAlgorithm;
1919
import org.pageseeder.diffx.api.DiffHandler;
20+
import org.pageseeder.diffx.api.Equality;
2021
import org.pageseeder.diffx.api.Operator;
2122

2223
import java.util.List;
@@ -37,7 +38,7 @@
3738
*
3839
* @author Christophe Lauret
3940
*
40-
* @version 1.3.0
41+
* @version 1.3.1
4142
* @since 0.9.0
4243
*/
4344
public final class HirschbergAlgorithm<T> implements DiffAlgorithm<T> {
@@ -47,6 +48,27 @@ public final class HirschbergAlgorithm<T> implements DiffAlgorithm<T> {
4748
*/
4849
private static final boolean DEBUG = false;
4950

51+
/**
52+
* Determines the strategy to compare elements for equality within the diff algorithm.
53+
*/
54+
private final Equality<T> eq;
55+
56+
/**
57+
* Default constructor using token equality.
58+
*/
59+
public HirschbergAlgorithm() {
60+
this.eq = T::equals;
61+
}
62+
63+
/**
64+
* Constructor specifying the equality strategy.
65+
*
66+
* @param eq The strategy to compare elements for equality.
67+
*/
68+
public HirschbergAlgorithm(Equality<T> eq) {
69+
this.eq = eq;
70+
}
71+
5072
@Override
5173
public void diff(List<? extends T> from, List<? extends T> to, DiffHandler<T> handler) {
5274
// It is more efficient to supply the sizes than retrieve from lists
@@ -58,12 +80,12 @@ public void diff(List<? extends T> from, List<? extends T> to, DiffHandler<T> ha
5880
*
5981
* @return the last line of the Needleman-Wunsch score matrix
6082
*/
61-
private static <T> int[] algorithmB(int m, int n, List<? extends T> a, List<? extends T> b) {
83+
private int[] algorithmB(int m, int n, List<? extends T> a, List<? extends T> b) {
6284
int[][] k = new int[2][n + 1];
6385
for (int i = 1; i <= m; i++) {
6486
if (n + 1 >= 0) System.arraycopy(k[1], 0, k[0], 0, n + 1);
6587
for (int j = 1; j <= n; j++) {
66-
if (a.get(i - 1).equals(b.get(j - 1))) {
88+
if (this.eq.equals(a.get(i - 1), b.get(j - 1))) {
6789
k[1][j] = k[0][j - 1] + 1;
6890
} else {
6991
k[1][j] = Math.max(k[1][j - 1], k[0][j]);
@@ -78,12 +100,12 @@ private static <T> int[] algorithmB(int m, int n, List<? extends T> a, List<? ex
78100
*
79101
* <p>Implementation note: we traverse the list in reverse, it is more efficient than reversing the lists.
80102
*/
81-
private static <T> int[] algorithmBRev(int m, int n, List<? extends T> a, List<? extends T> b) {
103+
private int[] algorithmBRev(int m, int n, List<? extends T> a, List<? extends T> b) {
82104
int[][] k = new int[2][n + 1];
83105
for (int i = m - 1; i >= 0; i--) {
84106
if (n + 1 >= 0) System.arraycopy(k[1], 0, k[0], 0, n + 1);
85107
for (int j = n - 1; j >= 0; j--) {
86-
if (a.get(i).equals(b.get(j))) {
108+
if (this.eq.equals(a.get(i), b.get(j))) {
87109
k[1][n - j] = k[0][n - j - 1] + 1;
88110
} else {
89111
k[1][n - j] = Math.max(k[1][n - j - 1], k[0][n - j]);
@@ -113,7 +135,7 @@ private static int findK(int[] l1, int[] l2, int n) {
113135
* Algorithm C as described by Hirschberg
114136
*/
115137
@SuppressWarnings("java:S106")
116-
private static <T> void algorithmC(int m, int n, List<? extends T> a, List<? extends T> b, DiffHandler<T> handler) {
138+
private void algorithmC(int m, int n, List<? extends T> a, List<? extends T> b, DiffHandler<T> handler) {
117139
if (DEBUG) System.out.print("[m=" + m + ",n=" + n + "," + a + "," + b + "] ->");
118140

119141
if (n == 0) {
@@ -133,7 +155,7 @@ private static <T> void algorithmC(int m, int n, List<? extends T> a, List<? ext
133155
boolean match = false;
134156
T a0 = a.get(0);
135157
for (int j = 0; j < n; j++) {
136-
if (a0.equals(b.get(j)) && !match) {
158+
if (this.eq.equals(a0, b.get(j)) && !match) {
137159
handler.handle(Operator.MATCH, a0);
138160
match = true;
139161
} else {

src/main/java/org/pageseeder/diffx/algorithm/KumarRanganAlgorithm.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.pageseeder.diffx.api.DiffAlgorithm;
1919
import org.pageseeder.diffx.api.DiffHandler;
20+
import org.pageseeder.diffx.api.Equality;
2021
import org.pageseeder.diffx.api.Operator;
2122

2223
import java.util.List;
@@ -35,18 +36,41 @@
3536
* XML sequences as it cannot always produce well-formed XML.
3637
*
3738
* @author Christophe Lauret
38-
* @version 0.9.0
39+
*
40+
* @version 1.3.1
41+
* @since 0.9.0
3942
*/
4043
public final class KumarRanganAlgorithm<T> implements DiffAlgorithm<T> {
4144

45+
/**
46+
* Determines the strategy to compare elements for equality within the diff algorithm.
47+
*/
48+
private final Equality<T> eq;
49+
50+
/**
51+
* Default constructor using token equality.
52+
*/
53+
public KumarRanganAlgorithm() {
54+
this.eq = T::equals;
55+
}
56+
57+
/**
58+
* Constructor specifying the equality strategy.
59+
*
60+
* @param eq The strategy to compare elements for equality.
61+
*/
62+
public KumarRanganAlgorithm(Equality<T> eq) {
63+
this.eq = eq;
64+
}
65+
4266
/**
4367
* Set to <code>true</code> to show debug info.
4468
*/
4569
private static final boolean DEBUG = false;
4670

4771
@Override
4872
public void diff(List<? extends T> from, List<? extends T> to, DiffHandler<T> handler) {
49-
Instance<T> instance = new Instance<>(from, to);
73+
Instance<T> instance = new Instance<>(from, to, eq);
5074
instance.process(handler);
5175
}
5276

@@ -78,6 +102,8 @@ private static class Instance<T> {
78102
private int R;
79103
private int S;
80104

105+
private final Equality<T> eq;
106+
81107
/**
82108
* A counter for the index of the second sequence when generating the diff.
83109
*/
@@ -91,9 +117,10 @@ private static class Instance<T> {
91117
*/
92118
private DiffHandler<T> handler;
93119

94-
Instance(List<? extends T> from, List<? extends T> to) {
120+
Instance(List<? extends T> from, List<? extends T> to, Equality<T> eq) {
95121
this.A = Objects.requireNonNull(from);
96122
this.B = Objects.requireNonNull(to);
123+
this.eq = eq;
97124
}
98125

99126
/**
@@ -235,8 +262,7 @@ private void fillOne(int startA, int endA, int startB, int endB, int m, int n, i
235262

236263
// The real index in the global char table is:
237264
// current_index * sign + beginning index of the sub-char array
238-
while (posB > lowerB && !this.A.get((i - 1) * sign + startA)
239-
.equals(this.B.get((posB - 1) * sign + startB))) {
265+
while (posB > lowerB && !this.eq.equals(this.A.get((i - 1) * sign + startA), this.B.get((posB - 1) * sign + startB))) {
240266
posB--;
241267
}
242268
int temp = Math.max(posB, lowerB);
@@ -361,7 +387,7 @@ private void computeLCSBaseCase(int startA, int endA, int startB, int endB, int
361387
int i = 0;
362388

363389
// 2. Start in order for the A subsequence and get the index of the B subsequence
364-
while (i < p && this.A.get(i + startA).equals(this.B.get(this.LL[p - i] - 1 + startB))) {
390+
while (i < p && this.eq.equals(this.A.get(i + startA), this.B.get(this.LL[p - i] - 1 + startB))) {
365391
this.handler.handle(Operator.MATCH, this.A.get(i + startA));
366392
this.J++;
367393
i++;
@@ -385,7 +411,7 @@ private void computeLCSBaseCase(int startA, int endA, int startB, int endB, int
385411
this.J++;
386412
i++;
387413

388-
while (i < m && this.J < endB && !this.A.get(i + startA).equals(this.B.get(this.J))) {
414+
while (i < m && this.J < endB && !this.eq.equals(this.A.get(i + startA), this.B.get(this.J))) {
389415
insertUpTo(this.J + 1);
390416
}
391417
}

src/main/java/org/pageseeder/diffx/algorithm/MatrixProcessor.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.pageseeder.diffx.algorithm;
1717

18+
import org.pageseeder.diffx.api.Equality;
19+
import org.pageseeder.diffx.token.XMLToken;
1820
import org.pageseeder.diffx.xml.Sequence;
1921

2022
import java.io.PrintStream;
@@ -24,7 +26,8 @@
2426
* Build the matrix for the specified tokens using dynamic programming.
2527
*
2628
* @author Christophe Lauret
27-
* @version 1.2.0
29+
*
30+
* @version 1.3.1
2831
* @since 0.9.0
2932
*/
3033
public final class MatrixProcessor<T> {
@@ -42,7 +45,18 @@ public void setInverse(boolean inverse) {
4245
* @return the matrix using dynamic programming
4346
*/
4447
public Matrix process(Sequence first, Sequence second) {
45-
return this.inverse ? computeInverse(first.tokens(), second.tokens()) : compute(first.tokens(), second.tokens());
48+
return process(first, second, XMLToken::equals);
49+
}
50+
51+
/**
52+
* @param first The first sequence of tokens to test.
53+
* @param second The second sequence of tokens to test.
54+
* @param eq The strategy to compare elements for equality.
55+
*
56+
* @return the matrix using dynamic programming
57+
*/
58+
public Matrix process(Sequence first, Sequence second, Equality<XMLToken> eq) {
59+
return this.inverse ? computeInverse(first.tokens(), second.tokens(), eq) : compute(first.tokens(), second.tokens(), eq);
4660
}
4761

4862
/**
@@ -52,10 +66,21 @@ public Matrix process(Sequence first, Sequence second) {
5266
* @return the matrix using dynamic programming
5367
*/
5468
public Matrix process(List<? extends T> first, List<? extends T> second) {
55-
return this.inverse ? computeInverse(first, second) : compute(first, second);
69+
return process(first, second, T::equals);
70+
}
71+
72+
/**
73+
* @param first The first sequence of tokens to test.
74+
* @param second The second sequence of tokens to test.
75+
* @param eq The strategy to compare elements for equality.
76+
*
77+
* @return the matrix using dynamic programming
78+
*/
79+
public Matrix process(List<? extends T> first, List<? extends T> second, Equality<T> eq) {
80+
return this.inverse ? computeInverse(first, second, eq) : compute(first, second, eq);
5681
}
5782

58-
private static <T> Matrix compute(List<? extends T> first, List<? extends T> second) {
83+
private static <T> Matrix compute(List<? extends T> first, List<? extends T> second, Equality<T> eq) {
5984
Matrix matrix = getMatrix(first, second, false);
6085
int length1 = first.size();
6186
int length2 = second.size();
@@ -67,7 +92,7 @@ private static <T> Matrix compute(List<? extends T> first, List<? extends T> sec
6792
if (i == 0 || j == 0) {
6893
matrix.set(i, j, 0);
6994
} else {
70-
if (first.get(i - 1).equals(second.get(j - 1))) {
95+
if (eq.equals(first.get(i - 1), second.get(j - 1))) {
7196
// the tokens are the same
7297
matrix.incrementPath(i, j);
7398
} else {
@@ -80,7 +105,7 @@ private static <T> Matrix compute(List<? extends T> first, List<? extends T> sec
80105
return matrix;
81106
}
82107

83-
private static <T> Matrix computeInverse(List<? extends T> first, List<? extends T> second) {
108+
private static <T> Matrix computeInverse(List<? extends T> first, List<? extends T> second, Equality<T> eq) {
84109
Matrix matrix = getMatrix(first, second, true);
85110
int length1 = first.size();
86111
int length2 = second.size();
@@ -92,7 +117,7 @@ private static <T> Matrix computeInverse(List<? extends T> first, List<? extends
92117
if (i >= length1 || j >= length2) {
93118
matrix.set(i, j, 0);
94119
} else {
95-
if (first.get(i).equals(second.get(j))) {
120+
if (eq.equals(first.get(i), second.get(j))) {
96121
// the tokens are the same
97122
matrix.incrementPath(i, j);
98123
} else {

0 commit comments

Comments
 (0)