Skip to content

Commit ef3c6ec

Browse files
committed
variant generation with macros
1 parent 51caa62 commit ef3c6ec

12 files changed

Lines changed: 174 additions & 47 deletions

File tree

src/main/java/org/variantsync/vevos/simulation/examples/GenerationExample.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public static void main(final String[] args) throws Resources.ResourceIOExceptio
132132
/// Moreover, VariantGenerationOptions allow to configure some parameters for the variant generation.
133133
//Here, we just instruct the generation to exit in case an error happens but we could for example also instruct it to ignore errors and proceed.
134134
final ArtefactFilter<SourceCodeFile> artefactFilter = ArtefactFilter.KeepAll();
135-
final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(artefactFilter);
135+
final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(false, artefactFilter);
136136

137137
/// Checkout the considered commit of the input SPL to access its source code.
138138
try {

src/main/java/org/variantsync/vevos/simulation/examples/VEVOSBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public static void benchmark(final Repo repo) throws Exception {
146146
logTime("Sampling " + variants.size() + " variants", timeSample);
147147

148148
final ArtefactFilter<SourceCodeFile> artefactFilter = ArtefactFilter.KeepAll();
149-
final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(artefactFilter);
149+
final VariantGenerationOptions generationOptions = VariantGenerationOptions.ExitOnError(false, artefactFilter);
150150

151151
clock.start();
152152
for (final Variant variant : variants) {

src/main/java/org/variantsync/vevos/simulation/io/TextIO.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
// TODO: Implement readLinesAs(Path p, Function<> f) with which one can load a file into a desired format
2020

2121
public class TextIO {
22+
public final static String LINEBREAK = "\r\n";
23+
public final static String LINEBREAK_REGEX = "\\r?\\n";
24+
2225
public static String[] readLinesAsArray(final File file) throws IOException {
2326
final LinkedList<String> lines = new LinkedList<>();
2427
try (final BufferedReader reader = new BufferedReader(new FileReader(file))) {
@@ -50,7 +53,20 @@ public static String readLastLine(final File file) throws IOException {
5053
* @return The lines that were read
5154
*/
5255
public static Result<List<String>, IOException> readLinesTrimmed(final Path p) {
53-
return Result.Try(() -> Files.readAllLines(p).stream().map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toList()));
56+
return readLines(p).map(lines ->
57+
lines.stream()
58+
.map(String::trim)
59+
.filter(s -> !s.isEmpty())
60+
.collect(Collectors.toList()));
61+
}
62+
63+
public static Result<List<String>, IOException> readLines(final Path p) {
64+
try (final BufferedReader br = new BufferedReader(new FileReader(p.toFile()));
65+
final Stream<String> linesStream = br.lines()) {
66+
return Result.Success(linesStream.toList());
67+
} catch (final IOException e) {
68+
return Result.Failure(e);
69+
}
5470
}
5571

5672
/**
@@ -65,16 +81,16 @@ public static void copyTextLines(final Path sourceFile, final Path targetFile, f
6581
/// Do not use Files.readAllLines(sourceFile) as it assumes the files to be in UTF-8 and crashes otherwise.
6682
// Do also not use try (final Stream<String> linesStream = new BufferedReader(new FileReader(sourceFile.toFile())).lines()) {
6783
// Apparently, Java is stupid and the BufferedReader is not closed by the try-with-resources if it is anonymous.
68-
try (BufferedReader br = new BufferedReader(new FileReader(sourceFile.toFile()));
69-
Stream<String> linesStream = br.lines()) {
70-
final List<String> read_lines = linesStream.collect(Collectors.toList());
84+
try (final BufferedReader br = new BufferedReader(new FileReader(sourceFile.toFile()));
85+
final Stream<String> linesStream = br.lines()) {
86+
final List<String> read_lines = linesStream.toList();
7187
final StringBuilder linesToWrite = new StringBuilder();
7288

7389
for (final Integer lineNo : linesToTake) {
74-
int lineIndex = lineNo - 1;
90+
final int lineIndex = lineNo - 1;
7591
// skip lines that exceed the content
7692
if (lineIndex >= read_lines.size()) {
77-
String logMessage = "Skipped copying line "
93+
final String logMessage = "Skipped copying line "
7894
+ lineNo
7995
+ " from \""
8096
+ sourceFile

src/main/java/org/variantsync/vevos/simulation/variability/pc/LineBasedAnnotation.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import org.variantsync.vevos.simulation.variability.pc.groundtruth.BlockMatching;
99
import org.variantsync.vevos.simulation.variability.pc.groundtruth.GroundTruth;
1010
import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions;
11+
import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantAnnotation;
12+
import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantLine;
13+
import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantLineChunk;
1114
import org.variantsync.vevos.simulation.variability.pc.visitor.LineBasedAnnotationVisitorFocus;
1215

1316
import java.util.ArrayList;
@@ -57,6 +60,12 @@ private static void addRange(final List<Integer> list, final int fromInclusive,
5760
}
5861
}
5962

63+
private static void addRange2(final List<VariantLineChunk> list, final int fromInclusive, final int toInclusive) {
64+
for (int i = fromInclusive; i <= toInclusive; ++i) {
65+
list.add(new VariantLine(i));
66+
}
67+
}
68+
6069
public int getLineFrom() {
6170
return lineFrom;
6271
}
@@ -176,6 +185,41 @@ public List<Integer> getAllLinesFor(final Predicate<LineBasedAnnotation> isInclu
176185
return chunksToWrite;
177186
}
178187

188+
/**
189+
* Computes all lines that should be included in the given variant when evaluating the annotations in this artefact.
190+
*
191+
* @param isIncluded A predicate to select subtrees. A subtree will be considered if isIncluded returns true for it.
192+
* @return All line numbers that should be copied from the SPL file to the variant file. 1-based.
193+
*/
194+
public VariantAnnotation getLinesToCopy(final Predicate<LineBasedAnnotation> isIncluded) {
195+
final List<VariantLineChunk> chunksToWrite = new ArrayList<>();
196+
// final List<Integer> chunksToWrite = new ArrayList<>();
197+
final int firstCodeLine = getLineFrom() + style.offset; // ignore #if
198+
final int lastCodeLine = getLineTo() - style.offset; // ignore #endif
199+
200+
int currentLine = firstCodeLine;
201+
for (final LineBasedAnnotation subtree : subtrees) {
202+
if (currentLine < subtree.getLineFrom()) {
203+
addRange2(chunksToWrite, currentLine, subtree.getLineFrom() - 1);
204+
}
205+
206+
if (isIncluded.test(subtree)) {
207+
chunksToWrite.add(subtree.getLinesToCopy(isIncluded));
208+
}
209+
210+
currentLine = subtree.getLineTo() + 1;
211+
}
212+
213+
if (currentLine <= lastCodeLine) {
214+
addRange2(chunksToWrite, currentLine, lastCodeLine);
215+
}
216+
217+
return new VariantAnnotation(
218+
this.getFeatureMapping(),
219+
chunksToWrite
220+
);
221+
}
222+
179223
public void simplify() {
180224
LineBasedAnnotationSimplifier.simplify(this);
181225
}

src/main/java/org/variantsync/vevos/simulation/variability/pc/SourceCodeFile.java

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,15 @@
1010
import org.variantsync.vevos.simulation.util.fide.bugfix.FixTrueFalse;
1111
import org.variantsync.vevos.simulation.util.io.CaseSensitivePath;
1212
import org.variantsync.vevos.simulation.util.io.PathUtils;
13-
import org.variantsync.vevos.simulation.variability.pc.groundtruth.AnnotationGroundTruth;
1413
import org.variantsync.vevos.simulation.variability.pc.groundtruth.BlockMatching;
1514
import org.variantsync.vevos.simulation.variability.pc.groundtruth.GroundTruth;
1615
import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions;
16+
import org.variantsync.vevos.simulation.variability.pc.variantlines.VariantAnnotation;
1717
import org.variantsync.vevos.simulation.variability.pc.visitor.SourceCodeFileVisitorFocus;
1818

1919
import java.io.FileNotFoundException;
20-
import java.io.IOException;
2120
import java.nio.file.Files;
2221
import java.util.Collections;
23-
import java.util.List;
24-
import java.util.Optional;
2522

2623
/**
2724
* Represents a variable source code file (e.g., because part of a plugin or only conditionally included).
@@ -57,36 +54,49 @@ public Result<GroundTruth, Exception> generateVariant(
5754
return Result.Failure(new FileNotFoundException("Source file " + sourceFile + " does not exist!"));
5855
}
5956

60-
final Result<Optional<AnnotationGroundTruth>, IOException> groundTruth =
57+
return
6158
// Create the target file.
6259
PathUtils.createEmptyAsResult(targetFile.path())
6360
// Write to target file.
64-
.bind(unit -> Traversable.sequence(rootAnnotation.deriveForVariant(variant).map(splAnnotationGroundTruth -> {
61+
.bind(unit -> Traversable.sequence(
62+
// Compute ground truth for our variant (i.e., make the variant feature-aware
63+
rootAnnotation
64+
.deriveForVariant(variant)
65+
.map(splAnnotationGroundTruth -> {
6566
final BlockMatching lineMatching = splAnnotationGroundTruth.matching();
66-
// only write lines of blocks that are part of our variant
67-
final List<Integer> lines = rootAnnotation.getAllLinesFor(lineMatching::isPresentInVariant);
68-
return Result.Try(
69-
() -> {
70-
TextIO.copyTextLines(sourceFile.path(), targetFile.path(), lines);
71-
return splAnnotationGroundTruth;
72-
});
73-
})));
74-
75-
return groundTruth.bimap(
76-
// In case of success, return ground truth.
77-
Functjonal.match(
78-
splAnnotationGroundTruth -> GroundTruth.forSourceCodeFile(
79-
new SourceCodeFile(getFeatureMapping(), getFile(), splAnnotationGroundTruth.variantArtefact()),
80-
splAnnotationGroundTruth
67+
// Retrieve all lines of code from the SPL file that should be included in the variant file.
68+
final VariantAnnotation variantCode = rootAnnotation.getLinesToCopy(lineMatching::isPresentInVariant);
69+
return TextIO
70+
// read all lines in the input SPL file
71+
.readLines(sourceFile.path())
72+
.bind(splLines -> Result.Try(() ->
73+
// write all lines that should be included in the variant to the text file
74+
TextIO.write(
75+
targetFile.path(),
76+
String.join(
77+
TextIO.LINEBREAK,
78+
//
79+
variantCode.project(strategy, splLines)
80+
)
81+
)
82+
))
83+
.map(unit2 -> splAnnotationGroundTruth);
84+
})))
85+
.bimap(
86+
// In case of success, return ground truth.
87+
Functjonal.match(
88+
splAnnotationGroundTruth -> GroundTruth.forSourceCodeFile(
89+
new SourceCodeFile(getFeatureMapping(), getFile(), splAnnotationGroundTruth.variantArtefact()),
90+
splAnnotationGroundTruth
91+
),
92+
() -> GroundTruth.withoutAnnotations(new SourceCodeFile(getFeatureMapping(), getFile()))
8193
),
82-
() -> GroundTruth.withoutAnnotations(new SourceCodeFile(getFeatureMapping(), getFile()))
83-
),
84-
// In case of failure, log it (and implicitly transform IOException to Exception).
85-
ioexception -> {
86-
Logger.error("Could not create variant file " + targetFile + " because ", ioexception);
87-
return ioexception;
88-
}
89-
);
94+
// In case of failure, log it (and implicitly transform IOException to Exception).
95+
ioexception -> {
96+
Logger.error("Could not create variant file " + targetFile + " because ", ioexception);
97+
return ioexception;
98+
}
99+
);
90100
}
91101

92102
public LineBasedAnnotation getRootAnnotation() {

src/main/java/org/variantsync/vevos/simulation/variability/pc/options/VariantGenerationOptions.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55
public record VariantGenerationOptions(
66
boolean exitOnError,
77
boolean ignoreNonExistentSPLFiles,
8+
boolean withMacros,
89
ArtefactFilter<SourceCodeFile> filter
910
)
1011
{
11-
public static VariantGenerationOptions ExitOnError(final ArtefactFilter<SourceCodeFile> filter) {
12-
return new VariantGenerationOptions(true, false, filter);
12+
public static VariantGenerationOptions ExitOnError(final boolean withMacros, final ArtefactFilter<SourceCodeFile> filter) {
13+
return new VariantGenerationOptions(true, false, withMacros, filter);
1314
}
1415

15-
public static VariantGenerationOptions IgnoreErrors(final ArtefactFilter<SourceCodeFile> filter) {
16-
return new VariantGenerationOptions(false, true, filter);
17-
}
18-
19-
public static VariantGenerationOptions ExitOnErrorButAllowNonExistentFiles(final ArtefactFilter<SourceCodeFile> filter) {
20-
return new VariantGenerationOptions(true, true, filter);
16+
public static VariantGenerationOptions ExitOnErrorButAllowNonExistentFiles(final boolean withMacros, final ArtefactFilter<SourceCodeFile> filter) {
17+
return new VariantGenerationOptions(true, true, withMacros, filter);
2118
}
2219
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.variantsync.vevos.simulation.variability.pc.variantlines;
2+
3+
public record ProjectionOptions(
4+
boolean withMacros
5+
) {
6+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.variantsync.vevos.simulation.variability.pc.variantlines;
2+
3+
import org.prop4j.Node;
4+
import org.prop4j.NodeWriter;
5+
import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
public record VariantAnnotation(
11+
Node condition,
12+
List<VariantLineChunk> lines
13+
) implements VariantLineChunk {
14+
@Override
15+
public List<String> project(final VariantGenerationOptions projectionOptions, final List<String> splFileLines) {
16+
final List<String> result = new ArrayList<>();
17+
18+
if (projectionOptions.withMacros()) {
19+
result.add("#if " + condition.toString(NodeWriter.javaSymbols));
20+
}
21+
22+
for (final VariantLineChunk child : lines) {
23+
result.addAll(child.project(projectionOptions, splFileLines));
24+
}
25+
26+
if (projectionOptions.withMacros()) {
27+
result.add("#endif");
28+
}
29+
30+
return result;
31+
}
32+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.variantsync.vevos.simulation.variability.pc.variantlines;
2+
3+
import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions;
4+
5+
import java.util.List;
6+
7+
public record VariantLine(Integer lineNumber) implements VariantLineChunk {
8+
@Override
9+
public List<String> project(final VariantGenerationOptions projectionOptions, final List<String> splFileLines) {
10+
return List.of(splFileLines.get(lineNumber - 1));
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.variantsync.vevos.simulation.variability.pc.variantlines;
2+
3+
import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions;
4+
5+
import java.util.List;
6+
7+
public interface VariantLineChunk {
8+
List<String> project(final VariantGenerationOptions projectionOptions, final List<String> splFileLines);
9+
}

0 commit comments

Comments
 (0)