Skip to content

Commit ec82fd4

Browse files
committed
fix: let GumTreeDiff update the line numbers in the labels
1 parent 411944b commit ec82fd4

4 files changed

Lines changed: 98 additions & 15 deletions

File tree

src/main/java/org/variantsync/diffdetective/variation/DiffLinesLabel.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import java.util.ArrayList;
44
import java.util.Arrays;
5+
import java.util.Iterator;
56
import java.util.List;
67
import java.util.stream.Collectors;
78

89
import org.variantsync.diffdetective.diff.text.DiffLineNumber;
910
import org.variantsync.diffdetective.util.Assert;
1011
import org.variantsync.diffdetective.util.StringUtils;
12+
import org.variantsync.diffdetective.variation.diff.Time;
1113
import org.variantsync.diffdetective.variation.diff.VariationDiff; // For Javadoc
1214

1315
/**
@@ -82,6 +84,67 @@ public List<String> getTrailingLines() {
8284
return getDiffTrailingLines().stream().map(Line::content).toList();
8385
}
8486

87+
/**
88+
* Returns a deep copy where the line numbers at {@code time} are set to
89+
* {@link DiffLineNumber#InvalidLineNumber}.
90+
*/
91+
@Override
92+
public DiffLinesLabel withoutTimeDependentState(Time time) {
93+
return new DiffLinesLabel(
94+
mapWithoutTimeDependentState(getDiffLines(), time),
95+
mapWithoutTimeDependentState(getDiffTrailingLines(), time)
96+
);
97+
}
98+
99+
private List<Line> mapWithoutTimeDependentState(List<Line> lines, Time time) {
100+
return lines
101+
.stream()
102+
.map(line -> new Line(
103+
line.content(),
104+
line.lineNumber().withLineNumberAtTime(DiffLineNumber.InvalidLineNumber, time)
105+
))
106+
.toList();
107+
}
108+
109+
/**
110+
* Returns a deep copy where the line numbers at {@code time} are copied from
111+
* {@code otherLabel}. The number of lines and their content must be identical in {@code this}
112+
* and {@code otherLabel}.
113+
*
114+
* @see withoutTimeDependentState
115+
*/
116+
@Override
117+
public DiffLinesLabel withTimeDependentStateFrom(Label otherLabel, Time time) {
118+
DiffLinesLabel other = (DiffLinesLabel) otherLabel;
119+
120+
return new DiffLinesLabel(
121+
zipWithTimeDependentStateFrom(this.getDiffLines(), other.getDiffLines(), time),
122+
zipWithTimeDependentStateFrom(this.getDiffTrailingLines(), other.getDiffTrailingLines(), time)
123+
);
124+
}
125+
126+
private static List<Line> zipWithTimeDependentStateFrom(List<Line> linesA, List<Line> linesB, Time time) {
127+
List<Line> result = new ArrayList<>(linesA.size());
128+
129+
Iterator<Line> itA = linesA.iterator();
130+
Iterator<Line> itB = linesA.iterator();
131+
while (itA.hasNext() && itB.hasNext()) {
132+
Line lineA = itA.next();
133+
Line lineB = itB.next();
134+
135+
Assert.assertEquals(lineA.content(), lineB.content());
136+
result.add(new Line(
137+
lineA.content(),
138+
lineB.lineNumber().withLineNumberAtTime(lineB.lineNumber().atTime(time), time)
139+
));
140+
}
141+
142+
Assert.assertFalse(itA.hasNext());
143+
Assert.assertFalse(itB.hasNext());
144+
145+
return result;
146+
}
147+
85148
@Override
86149
public String toString() {
87150
return lines

src/main/java/org/variantsync/diffdetective/variation/Label.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.List;
44

5+
import org.variantsync.diffdetective.util.Assert;
6+
import org.variantsync.diffdetective.variation.diff.Time;
57
import org.variantsync.diffdetective.variation.diff.VariationDiff; // For Javadoc
68
import org.variantsync.diffdetective.variation.tree.VariationTree; // For Javadoc
79

@@ -19,6 +21,29 @@ public interface Label {
1921
* For example, {@code #endif}s are stored as trailing lines.
2022
*/
2123
List<String> getTrailingLines();
24+
25+
/**
26+
* Returns a deep copy where the state that is only valid at {@code time} is set to its default
27+
* value. Note that not all implementations need to have time dependent state.
28+
*
29+
* @see withTimeDependentStateFrom
30+
*/
31+
default Label withoutTimeDependentState(Time time) {
32+
return this.clone();
33+
}
34+
35+
/**
36+
* Returns a deep copy where the state that is only valid at {@code time} is copied from
37+
* {@code other}. All time independent state must be equal in {@code this} and {@code other}.
38+
* Note that not all implementations need to have time dependent state.
39+
*
40+
* @see withoutTimeDependentState
41+
*/
42+
default Label withTimeDependentStateFrom(Label other, Time time) {
43+
Assert.assertEquals(this, other);
44+
return this.clone();
45+
}
46+
2247
/**
2348
* Creates a deep copy of this label.
2449
*/

src/main/java/org/variantsync/diffdetective/variation/diff/DiffNode.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import org.variantsync.functjonal.Cast;
1515

1616
import java.util.*;
17-
import java.util.function.BiFunction;
1817
import java.util.function.Function;
1918
import java.util.stream.Stream;
2019

@@ -393,12 +392,13 @@ public DiffNode<L> split(Time time) {
393392
getFromLine().as(otherDiffType),
394393
getToLine().as(otherDiffType),
395394
getFormula(),
396-
Cast.unchecked(label.clone())
395+
Cast.unchecked(label.withoutTimeDependentState(time.other()))
397396
);
398397

399398
this.diffType = otherDiffType.inverse();
400399
this.from = this.from.as(this.diffType);
401400
this.to = this.to.as(this.diffType);
401+
this.setLabel(Cast.unchecked(this.getLabel().withoutTimeDependentState(time)));
402402

403403
other.addChildren(this.removeChildren(time), time);
404404
getParent(time).replaceChild(this, other, time);
@@ -417,14 +417,13 @@ public DiffNode<L> split(Time time) {
417417
* Merges {@code other} into this node.
418418
* {@code other} is removed from the graph and this node inherits all of its edges. This
419419
* node and {@code other} need to be compatible (exist at different times and have the
420-
* same {@link getNodeType node type}).
420+
* same {@link getNodeType node type} and compatible {@link getLabel labels}).
421421
* <p>
422422
* Both {@code this} and {@code other} must not be {@link isRoot the root}.
423423
*
424424
* @param other the node which is removed from the graph
425-
* @param joinLabels returns a label that should represent the two passed labels
426425
*/
427-
public void join(DiffNode<L> other, BiFunction<L, L, L> joinLabels) {
426+
public void join(DiffNode<L> other) {
428427
Time time = switch (diffType) {
429428
case ADD -> BEFORE;
430429
case REM -> AFTER;
@@ -436,7 +435,7 @@ public void join(DiffNode<L> other, BiFunction<L, L, L> joinLabels) {
436435
Assert.assertFalse(other.isRoot());
437436

438437
diffType = DiffType.NON;
439-
label.setInnerLabel(joinLabels.apply(getLabel(), other.getLabel()));
438+
setLabel(Cast.unchecked(getLabel().withTimeDependentStateFrom(other.getLabel(), time)));
440439

441440
setFromLine(getFromLine().withLineNumberAtTime(other.getFromLine().atTime(AFTER), AFTER));
442441
setToLine(getToLine().withLineNumberAtTime(other.getToLine().atTime(AFTER), AFTER));

src/main/java/org/variantsync/diffdetective/variation/diff/construction/GumTreeDiff.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.variantsync.diffdetective.util.Assert;
1212
import org.variantsync.diffdetective.variation.Label;
1313
import org.variantsync.diffdetective.variation.diff.DiffNode;
14+
import org.variantsync.diffdetective.variation.diff.Time;
1415
import org.variantsync.diffdetective.variation.diff.VariationDiff;
1516
import org.variantsync.diffdetective.variation.diff.source.VariationTreeDiffSource;
1617
import org.variantsync.diffdetective.variation.diff.traverse.VariationDiffTraversal;
@@ -22,7 +23,6 @@
2223
import java.util.Map;
2324

2425
import static org.variantsync.diffdetective.variation.diff.DiffType.ADD;
25-
import static org.variantsync.diffdetective.variation.diff.DiffType.REM;
2626
import static org.variantsync.diffdetective.variation.diff.Time.AFTER;
2727
import static org.variantsync.diffdetective.variation.diff.Time.BEFORE;
2828

@@ -124,8 +124,7 @@ private static <L extends Label> void removeUnmapped(MappingStore mappings, Vari
124124
Tree dst = mappings.getDstForSrc(node);
125125
if (dst == null || !dst.getLabel().equals(node.getLabel())) {
126126
var diffNode = Cast.<Tree, VariationDiffAdapter<L>>unchecked(node).getDiffNode();
127-
diffNode.diffType = REM;
128-
diffNode.drop(AFTER);
127+
diffNode.split(AFTER).drop();
129128
}
130129
}
131130
}
@@ -157,7 +156,7 @@ private static <L extends Label> void addUnmapped(MappingStore mappings, DiffNod
157156
new DiffLineNumber(DiffLineNumber.InvalidLineNumber, from, from),
158157
new DiffLineNumber(DiffLineNumber.InvalidLineNumber, to, to),
159158
variationNode.getFormula(),
160-
Cast.unchecked(variationNode.getLabel().clone())
159+
Cast.unchecked(variationNode.getLabel().withoutTimeDependentState(BEFORE))
161160
);
162161
} else {
163162
diffNode = Cast.<Tree, VariationDiffAdapter<L>>unchecked(src).getDiffNode();
@@ -168,6 +167,7 @@ private static <L extends Label> void addUnmapped(MappingStore mappings, DiffNod
168167

169168
diffNode.setFromLine(diffNode.getFromLine().withLineNumberAtTime(afterNode.getVariationNode().getLineRange().fromInclusive(), AFTER));
170169
diffNode.setToLine(diffNode.getToLine().withLineNumberAtTime(afterNode.getVariationNode().getLineRange().toExclusive(), AFTER));
170+
diffNode.setLabel(Cast.unchecked(diffNode.getLabel().withTimeDependentStateFrom(afterNode.getVariationNode().getLabel(), Time.AFTER)));
171171
}
172172
parent.addChild(diffNode, AFTER);
173173

@@ -220,11 +220,7 @@ public static <L extends Label> DiffNode<L> improveMatching(DiffNode<L> tree, Ma
220220
afterNode.split(BEFORE);
221221
}
222222

223-
beforeNode.join(afterNode, (beforeLabel, afterLabel) -> {
224-
Assert.assertEquals(beforeLabel.getLines(), afterLabel.getLines());
225-
Assert.assertEquals(beforeLabel.getTrailingLines(), afterLabel.getTrailingLines());
226-
return beforeLabel;
227-
});
223+
beforeNode.join(afterNode);
228224
}
229225

230226
Assert.assertTrue(beforeNode.isNon());

0 commit comments

Comments
 (0)