Skip to content

Commit 9e34ac5

Browse files
feat: extract before and after annotations for each line to account for PC changes
1 parent 1718348 commit 9e34ac5

8 files changed

Lines changed: 176 additions & 74 deletions

File tree

src/main/java/org/variantsync/vevos/extraction/BlockAnnotation.java

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,22 @@ public final class BlockAnnotation implements Serializable {
1919
private static final Pattern variableEnd = Pattern.compile("}");
2020
private static final Pattern quotation = Pattern.compile("\"");
2121
private static final Pattern semicolon = Pattern.compile(";");
22-
private final String featureMapping;
23-
private final String presenceCondition;
22+
private final FeatureMapping featureMappingBefore;
23+
private final PresenceCondition presenceConditionBefore;
24+
private final FeatureMapping featureMappingAfter;
25+
private final PresenceCondition presenceConditionAfter;
2426
private int lineStartInclusive;
2527
private int lineEndInclusive;
2628

27-
public BlockAnnotation(int lineStartInclusive, int lineEndInclusive, String featureMapping, String presenceCondition) {
29+
public BlockAnnotation(int lineStartInclusive, int lineEndInclusive,
30+
FeatureMapping featureMappingBefore, PresenceCondition presenceConditionBefore,
31+
FeatureMapping featureMappingAfter, PresenceCondition presenceConditionAfter) {
2832
this.lineStartInclusive = lineStartInclusive;
2933
this.lineEndInclusive = lineEndInclusive;
30-
this.featureMapping = featureMapping;
31-
this.presenceCondition = presenceCondition;
34+
this.featureMappingBefore = featureMappingBefore;
35+
this.presenceConditionBefore = presenceConditionBefore;
36+
this.featureMappingAfter = featureMappingAfter;
37+
this.presenceConditionAfter = presenceConditionAfter;
3238
}
3339

3440
public void setLineStartInclusive(int lineStartInclusive) {
@@ -48,32 +54,55 @@ public int lineEndExclusive() {
4854
}
4955

5056
@Override
51-
public boolean equals(Object obj) {
52-
if (obj == this) return true;
53-
if (obj == null || obj.getClass() != this.getClass()) return false;
54-
var that = (BlockAnnotation) obj;
55-
return this.lineStartInclusive == that.lineStartInclusive &&
56-
this.lineEndInclusive == that.lineEndInclusive &&
57-
Objects.equals(this.featureMapping, that.featureMapping) &&
58-
Objects.equals(this.presenceCondition, that.presenceCondition);
57+
public boolean equals(Object o) {
58+
if (this == o) return true;
59+
if (o == null || getClass() != o.getClass()) return false;
60+
BlockAnnotation that = (BlockAnnotation) o;
61+
return lineStartInclusive == that.lineStartInclusive
62+
&& lineEndInclusive == that.lineEndInclusive
63+
&& Objects.equals(featureMappingBefore, that.featureMappingBefore)
64+
&& Objects.equals(presenceConditionBefore, that.presenceConditionBefore)
65+
&& Objects.equals(featureMappingAfter, that.featureMappingAfter)
66+
&& Objects.equals(presenceConditionAfter, that.presenceConditionAfter);
5967
}
6068

6169
@Override
6270
public int hashCode() {
63-
return Objects.hash(lineStartInclusive, lineEndInclusive, featureMapping, presenceCondition);
71+
return Objects.hash(featureMappingBefore, presenceConditionBefore, featureMappingAfter, presenceConditionAfter, lineStartInclusive, lineEndInclusive);
72+
}
73+
74+
public boolean annotationEquals(BlockAnnotation other) {
75+
return this.featureMappingBefore.equals(other.featureMappingBefore)
76+
&& this.featureMappingAfter.equals(other.featureMappingAfter)
77+
&& this.presenceConditionBefore.equals(other.presenceConditionBefore)
78+
&& this.presenceConditionAfter.equals(other.presenceConditionAfter);
79+
}
80+
81+
public boolean annotationEquals(LineAnnotation other) {
82+
return this.featureMappingBefore.equals(other.featureMappingBefore())
83+
&& this.featureMappingAfter.equals(other.featureMappingAfter())
84+
&& this.presenceConditionBefore.equals(other.presenceConditionBefore())
85+
&& this.presenceConditionAfter.equals(other.presenceConditionAfter());
6486
}
6587

6688
@Override
6789
public String toString() {
6890
return "BlockAnnotation[" +
6991
"lineStartInclusive=" + lineStartInclusive + ", " +
7092
"lineEndExclusive=" + lineEndInclusive + ", " +
71-
"featureMapping=" + featureMapping + ", " +
72-
"presenceCondition=" + presenceCondition + ']';
93+
"featureMappingBefore=" + featureMappingBefore + ", " +
94+
"presenceConditionBefore=" + presenceConditionBefore + ", " +
95+
"featureMappingAfter=" + featureMappingAfter + ", " +
96+
"presenceConditionAfter=" + presenceConditionAfter + ']';
7397
}
7498

7599
public String asCSVLine() {
76-
return "%s;%s;%d;%d".formatted(normalizeCondition(this.featureMapping), normalizeCondition(this.presenceCondition), this.lineStartInclusive, this.lineEndInclusive);
100+
return "%s;%s;%s;%s;%d;%d".formatted(normalizeCondition(this.featureMappingBefore.mapping()),
101+
normalizeCondition(this.presenceConditionBefore.condition()),
102+
normalizeCondition(this.featureMappingAfter.mapping()),
103+
normalizeCondition(this.presenceConditionAfter.condition()),
104+
this.lineStartInclusive,
105+
this.lineEndInclusive);
77106
}
78107

79108
/**
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.variantsync.vevos.extraction;
2+
3+
public record FeatureMapping(String mapping) {
4+
5+
@Override
6+
public String toString() {
7+
return mapping;
8+
}
9+
}

src/main/java/org/variantsync/vevos/extraction/FileGT.java

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.variantsync.vevos.extraction;
22

3+
import org.tinylog.Logger;
34
import org.variantsync.diffdetective.util.Assert;
45
import org.variantsync.diffdetective.util.LineRange;
56

@@ -12,17 +13,17 @@
1213
public class FileGT implements Iterable<LineAnnotation>, Serializable {
1314
// The name of the file (its relative path from the root of the repo)
1415
protected final String file;
15-
// List of annotation for each line
16-
private final ArrayList<LineAnnotation> annotations;
1716
// A matching of this file's lines to counterparts associated with the same commit
1817
// i.e., the matching line numbers before or after the changes have been applied
1918
// a match is -1 if there is no counterpart
2019
protected final ArrayList<Integer> matching;
21-
// The set of variables occurring in the annotations of this file
22-
private final Set<String> variables;
2320
// Set of annotation block starts and ends
2421
protected final HashSet<Integer> blockEnds;
2522
protected final HashSet<Integer> blockStarts;
23+
// List of annotation for each line
24+
private final ArrayList<LineAnnotation> annotations;
25+
// The set of variables occurring in the annotations of this file
26+
private final Set<String> variables;
2627
// We can only use the before mapping until its being mutated
2728
protected boolean consumed;
2829

@@ -83,7 +84,8 @@ protected LineAnnotation insert(int index, LineAnnotation annotation) {
8384

8485
/**
8586
* Set the match for the given line number.
86-
* @param lineNumber The line number in the current version of the file
87+
*
88+
* @param lineNumber The line number in the current version of the file
8789
* @param matchedLine The matching line number in the counterpart version of the file
8890
*/
8991
protected void setMatching(int lineNumber, int matchedLine) throws MatchingException {
@@ -92,7 +94,10 @@ protected void setMatching(int lineNumber, int matchedLine) throws MatchingExcep
9294
return;
9395
}
9496
if (this.matching.get(lineNumber) != -1 && this.matching.get(lineNumber) != matchedLine) {
95-
throw new MatchingException("line number mismatch for " + this.file + " -- " + lineNumber
97+
// TODO: Handle this case
98+
// throw new MatchingException("line number mismatch for " + this.file + " -- " + lineNumber
99+
// + " : (" + this.matching.get(lineNumber) + " vs. " + matchedLine + ")");
100+
Logger.error("line number mismatch for " + this.file + " -- " + lineNumber
96101
+ " : (" + this.matching.get(lineNumber) + " vs. " + matchedLine + ")");
97102
}
98103
this.matching.set(lineNumber, matchedLine);
@@ -161,7 +166,8 @@ public Mutable insert(LineAnnotation line) {
161166

162167
/**
163168
* Set the matching of line numbers between current file version and counterpart file version.
164-
* @param currentRange The range of lines in the current file version
169+
*
170+
* @param currentRange The range of lines in the current file version
165171
* @param counterpartRange The range of lines in the counterpart file version
166172
*/
167173
public void setMatching(LineRange currentRange, LineRange counterpartRange) throws MatchingException {
@@ -174,9 +180,9 @@ public void setMatching(LineRange currentRange, LineRange counterpartRange) thro
174180

175181
if (matchedLine != -1) {
176182
// If there is a match, the matched regions must have the same size
177-
if(endNumber-lineNumber != matchEnd-matchedLine) {
183+
if (endNumber - lineNumber != matchEnd - matchedLine) {
178184
throw new MatchingException("line number mismatch for file" + this.file + " -- ; " +
179-
"ranges have different size " + (endNumber-lineNumber) + " : " + (matchEnd-matchedLine));
185+
"ranges have different size " + (endNumber - lineNumber) + " : " + (matchEnd - matchedLine));
180186
}
181187
// Set the matches
182188
for (; lineNumber < currentRange.toExclusive(); lineNumber++, matchedLine++) {
@@ -273,7 +279,7 @@ private static String csvMatchingLines(Complete complete) {
273279
private static ArrayList<BlockAnnotation> aggregateBlocks(Complete complete) {
274280
ArrayList<BlockAnnotation> blocks = new ArrayList<>();
275281
// The root annotation is always true and covers all lines
276-
BlockAnnotation rootBlock = new BlockAnnotation(1, complete.size(), "True", "True");
282+
BlockAnnotation rootBlock = new BlockAnnotation(1, complete.size(), new FeatureMapping("True"), new PresenceCondition("True"), new FeatureMapping("True"), new PresenceCondition("True"));
277283

278284
LinkedList<BlockAnnotation> blockStack = new LinkedList<>();
279285
blockStack.push(rootBlock);
@@ -282,24 +288,36 @@ private static ArrayList<BlockAnnotation> aggregateBlocks(Complete complete) {
282288

283289
if (blockStack.isEmpty()) {
284290
// Push a new block onto the stack
285-
blockStack.push(new BlockAnnotation(line.lineNumber(), line.lineNumber(), line.featureMapping(), line.presenceCondition()));
286-
continue;
291+
// blockStack.push(new BlockAnnotation(line.lineNumber(), line.lineNumber(), line.featureMappingBefore(),
292+
// line.presenceConditionBefore(), line.featureMappingAfter(), line.presenceConditionAfter()));
293+
// continue;
294+
throw new IllegalStateException();
287295
}
288296

297+
// Does the next line have a different annotation than the current block?
298+
boolean annotationChange = !blockStack.peek().annotationEquals(line) && !blockStack.peek().equals(rootBlock);
299+
289300
// new block, we have to unwind the stack in reverse order to find all completed blocks
290-
if (complete.blockEnds.contains(line.lineNumber())) {
301+
if (annotationChange) {
291302
// The current line is nested in retrieved block
292303
// Collect a completed block
293304
BlockAnnotation block = blockStack.pop();
294305
block.setLineEndInclusive(line.lineNumber() - 1);
295306
blocks.add(block);
296307
}
297308

309+
if (blockStack.peek() == null) {
310+
continue;
311+
}
312+
313+
// Does the next line have a different annotation than the current block?
314+
annotationChange = !blockStack.peek().annotationEquals(line);
315+
298316
Assert.assertTrue(blockStack.peekFirst() != null, () -> "%s\nProblem while processing line %d of file %s".formatted(complete.toString(), line.lineNumber(), complete.file));
299317
// If the current line is in a new block
300-
if (complete.blockStarts.contains(line.lineNumber())) {
318+
if (annotationChange) {
301319
// Push a new block onto the stack
302-
blockStack.push(new BlockAnnotation(line.lineNumber(), line.lineNumber(), line.featureMapping(), line.presenceCondition()));
320+
blockStack.push(new BlockAnnotation(line.lineNumber(), line.lineNumber(), line.featureMappingBefore(), line.presenceConditionBefore(), line.featureMappingAfter(), line.presenceConditionAfter()));
303321
}
304322
}
305323
// Unwind the stack fully
@@ -335,7 +353,9 @@ public String csvPCLines() {
335353
*
336354
* @return A String with the line matchings in csv format
337355
*/
338-
public String csvMatchingLines() { return this.csvMatchingText; }
356+
public String csvMatchingLines() {
357+
return this.csvMatchingText;
358+
}
339359

340360
/**
341361
* @return The list of block annotations for this file.

src/main/java/org/variantsync/vevos/extraction/GroundTruth.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,36 @@
88
/**
99
* The ground truth for the files of a repository at a specific commit (i.e., version).
1010
*
11-
* @param fileGTs The ground truths for each file
11+
* @param fileGTs The ground truths for each file
1212
* @param variables The set of variables that can appear in the presence conditions
1313
*/
1414
public record GroundTruth(HashMap<String, FileGT> fileGTs, Set<String> variables)
15-
implements Serializable {
15+
implements Serializable {
1616
private static final Pattern variableStart = Pattern.compile("\\$\\{");
1717
private static final Pattern variableEnd = Pattern.compile("}");
1818
private static final Pattern quotation = Pattern.compile("\"");
1919
private static final Pattern semicolon = Pattern.compile(";");
2020

21+
private static String variablesListAsString(Set<String> variables) {
22+
List<String> variablesList = new ArrayList<>(variables);
23+
Collections.sort(variablesList);
24+
25+
StringBuilder sb = new StringBuilder();
26+
for (String name : variablesList) {
27+
if (name.equals("True") || name.equals("False")) {
28+
continue;
29+
}
30+
name = name.replaceAll(variableStart.pattern(), "");
31+
name = name.replaceAll(variableEnd.pattern(), "");
32+
name = name.replaceAll(quotation.pattern(), "");
33+
name = name.replaceAll(semicolon.pattern(), "SEMICOLON");
34+
sb.append(name).append(System.lineSeparator());
35+
}
36+
return sb.toString();
37+
}
38+
2139
public FileGT computeIfAbsent(String file,
22-
Function<? super String, ? extends FileGT> mappingFunction) {
40+
Function<? super String, ? extends FileGT> mappingFunction) {
2341
return this.fileGTs.computeIfAbsent(file, mappingFunction);
2442
}
2543

@@ -65,27 +83,9 @@ public String combinedVariablesListAsString(GroundTruth other) {
6583
return variablesListAsString(variables);
6684
}
6785

68-
private static String variablesListAsString(Set<String> variables) {
69-
List<String> variablesList = new ArrayList<>(variables);
70-
Collections.sort(variablesList);
71-
72-
StringBuilder sb = new StringBuilder();
73-
for (String name : variablesList) {
74-
if (name.equals("True") || name.equals("False")) {
75-
continue;
76-
}
77-
name = name.replaceAll(variableStart.pattern(), "");
78-
name = name.replaceAll(variableEnd.pattern(), "");
79-
name = name.replaceAll(quotation.pattern(), "");
80-
name = name.replaceAll(semicolon.pattern(), "SEMICOLON");
81-
sb.append(name).append(System.lineSeparator());
82-
}
83-
return sb.toString();
84-
}
85-
8686
public String asPcCsvString() {
8787
return generateCsv(
88-
"Path;File Condition;Block Condition;Presence Condition;start;end",
88+
"Path;File Condition;Block Condition Before;Presence Condition Before;Block Condition After;Presence Condition After;start;end",
8989
FileGT.Complete::csvPCLines
9090
);
9191
}

src/main/java/org/variantsync/vevos/extraction/LineAnnotation.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,41 @@
88
/**
99
* Represents the ground truth annotation for a single line in a file
1010
* @param lineNumber
11-
* @param featureMapping
12-
* @param presenceCondition
11+
* @param featureMappingBefore
12+
* @param presenceConditionBefore
1313
* @param nodeType
1414
*/
15-
public record LineAnnotation(int lineNumber, String featureMapping, String presenceCondition, String nodeType, Set<String> uniqueContainedFeatures) implements Serializable {
16-
public final static LineAnnotation EMPTY = new LineAnnotation(-1, "True", "True", "", Collections.singleton("True"));
15+
public record LineAnnotation(int lineNumber,
16+
FeatureMapping featureMappingBefore, PresenceCondition presenceConditionBefore,
17+
FeatureMapping featureMappingAfter, PresenceCondition presenceConditionAfter,
18+
String nodeType, Set<String> uniqueContainedFeatures) implements Serializable {
19+
public final static LineAnnotation EMPTY = new LineAnnotation(-1,
20+
new FeatureMapping("True"), new PresenceCondition("True"),
21+
new FeatureMapping("True"), new PresenceCondition("True"),
22+
"", Collections.singleton("True"));
1723

1824
public int index() {
1925
return this.lineNumber-1;
2026
}
2127

2228
public LineAnnotation withOffset(int offset) {
23-
return new LineAnnotation(this.lineNumber + offset, this.featureMapping, this.presenceCondition, this.nodeType, this.uniqueContainedFeatures);
29+
return new LineAnnotation(this.lineNumber + offset,
30+
this.featureMappingBefore, this.presenceConditionBefore,
31+
this.featureMappingAfter, this.presenceConditionAfter,
32+
this.nodeType, this.uniqueContainedFeatures);
2433
}
2534

2635
public static LineAnnotation rootAnnotation(int lineNumber) {
27-
return new LineAnnotation(lineNumber, "True", "True", "ROOT", Collections.singleton("True"));
36+
return new LineAnnotation(lineNumber,
37+
new FeatureMapping("True"), new PresenceCondition("True"),
38+
new FeatureMapping("True"), new PresenceCondition("True"),
39+
"ROOT", Collections.singleton("True"));
2840
}
2941

3042
@Override
3143
public String toString() {
32-
return "%d, %s, FM =%s, PC = %s".formatted(lineNumber, nodeType, featureMapping, presenceCondition);
44+
return "%d, %s, FM Before =%s, PC Before = %s, FM After =%s, PC After =%s".formatted(lineNumber, nodeType,
45+
featureMappingBefore, presenceConditionBefore, featureMappingAfter, presenceConditionAfter);
3346
}
3447

3548
}

0 commit comments

Comments
 (0)