Skip to content

Commit a848074

Browse files
Merge pull request #5 from VariantSync/macros_in_variants
Macros in Variants
2 parents 806d60a + c6f8f40 commit a848074

15 files changed

Lines changed: 168 additions & 103 deletions

File tree

Binary file not shown.

repo/org/variantsync/functjonal/1.0-SNAPSHOT/maven-metadata-local.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
<snapshot>
88
<localCopy>true</localCopy>
99
</snapshot>
10-
<lastUpdated>20220405113508</lastUpdated>
10+
<lastUpdated>20220629093021</lastUpdated>
1111
<snapshotVersions>
1212
<snapshotVersion>
1313
<extension>jar</extension>
1414
<value>1.0-SNAPSHOT</value>
15-
<updated>20220405113508</updated>
15+
<updated>20220629093021</updated>
1616
</snapshotVersion>
1717
<snapshotVersion>
1818
<extension>pom</extension>
1919
<value>1.0-SNAPSHOT</value>
20-
<updated>20220405113508</updated>
20+
<updated>20220629093021</updated>
2121
</snapshotVersion>
2222
</snapshotVersions>
2323
</versioning>

repo/org/variantsync/functjonal/maven-metadata-local.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
<versions>
77
<version>1.0-SNAPSHOT</version>
88
</versions>
9-
<lastUpdated>20220405113508</lastUpdated>
9+
<lastUpdated>20220629093021</lastUpdated>
1010
</versioning>
1111
</metadata>

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: 26 additions & 52 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,60 +53,19 @@ 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()));
5461
}
5562

56-
/**
57-
* Extract all lines in [lineFrom, lineTo] = i from sourceFile and put them into targetFile for each interval i in linesToTake.
58-
*
59-
* @param sourceFile File from which to read lines. Won't be altered.
60-
* @param targetFile File to write the desired lines to.
61-
* @param linesToTake Intervals of lines to copy.
62-
* @throws IOException May occur upon writing or creating files.
63-
*/
64-
public static void copyTextLines(final Path sourceFile, final Path targetFile, final List<Integer> linesToTake) throws IOException {
65-
/// Do not use Files.readAllLines(sourceFile) as it assumes the files to be in UTF-8 and crashes otherwise.
66-
// Do also not use try (final Stream<String> linesStream = new BufferedReader(new FileReader(sourceFile.toFile())).lines()) {
67-
// 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());
71-
final StringBuilder linesToWrite = new StringBuilder();
72-
73-
for (final Integer lineNo : linesToTake) {
74-
int lineIndex = lineNo - 1;
75-
// skip lines that exceed the content
76-
if (lineIndex >= read_lines.size()) {
77-
String logMessage = "Skipped copying line "
78-
+ lineNo
79-
+ " from \""
80-
+ sourceFile
81-
+ "\" to \""
82-
+ targetFile
83-
+ "\" as it is out of bounds [1, "
84-
+ read_lines.size()
85-
+ "]!";
86-
87-
if (lineIndex > read_lines.size()) {
88-
// This was logged frequently and is caused by https://bugs.openjdk.java.net/browse/JDK-8199413
89-
// Skipping the line really is the best solution, as the empty line is created by appending a line separator
90-
// to the previous line. I added the additional if-statement, to only catch cases in which more than one line
91-
// is out of bounds, which indicates a serious problem.
92-
Logger.error(logMessage);
93-
}
94-
} else {
95-
// The list read_lines is 0-based.
96-
// Given lines are 1-based because line numbers are typically given 1-based.
97-
// Thus, we have to -1 here because line numbers are 1-indexed
98-
// but we want to look them up in read_lines, which is 0-based.
99-
linesToWrite.append(read_lines.get(lineIndex)).append(System.lineSeparator());
100-
}
101-
}
102-
103-
Files.write(
104-
targetFile,
105-
linesToWrite.toString().getBytes(),
106-
StandardOpenOption.APPEND);
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);
10769
}
10870
}
10971

@@ -120,6 +82,18 @@ public static void write(final Path p, final String text) throws IOException {
12082
Files.writeString(p, text, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW);
12183
}
12284

85+
/**
86+
* Append the given text to the given file.
87+
* Assumes that the given file already exists.
88+
*
89+
* @param p Existing file to append text to.
90+
* @param text Text to write to file.
91+
* @throws IOException if an I/O error occurs while writing to the file, or the text cannot be encoded using the specified charset.
92+
*/
93+
public static void append(final Path p, final String text) throws IOException {
94+
Files.writeString(p, text, StandardCharsets.UTF_8, StandardOpenOption.APPEND);
95+
}
96+
12397
public static String readAsString(final Path p) throws IOException {
12498
try (final BufferedReader reader = new BufferedReader(new FileReader(p.toFile()))) {
12599
return reader.lines().collect(Collectors.joining());

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

Lines changed: 13 additions & 6 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;
@@ -51,9 +54,9 @@ public LineBasedAnnotation(final LineBasedAnnotation other) {
5154
this.style = other.style;
5255
}
5356

54-
private static void addRange(final List<Integer> list, final int fromInclusive, final int toInclusive) {
57+
private static void addRange(final List<VariantLineChunk> list, final int fromInclusive, final int toInclusive) {
5558
for (int i = fromInclusive; i <= toInclusive; ++i) {
56-
list.add(i);
59+
list.add(new VariantLine(i));
5760
}
5861
}
5962

@@ -151,8 +154,9 @@ private Optional<LineBasedAnnotation> deriveForVariant(final Variant variant, in
151154
* @param isIncluded A predicate to select subtrees. A subtree will be considered if isIncluded returns true for it.
152155
* @return All line numbers that should be copied from the SPL file to the variant file. 1-based.
153156
*/
154-
public List<Integer> getAllLinesFor(final Predicate<LineBasedAnnotation> isIncluded) {
155-
final List<Integer> chunksToWrite = new ArrayList<>();
157+
public VariantAnnotation getLinesToCopy(final Predicate<LineBasedAnnotation> isIncluded) {
158+
final List<VariantLineChunk> chunksToWrite = new ArrayList<>();
159+
// final List<Integer> chunksToWrite = new ArrayList<>();
156160
final int firstCodeLine = getLineFrom() + style.offset; // ignore #if
157161
final int lastCodeLine = getLineTo() - style.offset; // ignore #endif
158162

@@ -163,7 +167,7 @@ public List<Integer> getAllLinesFor(final Predicate<LineBasedAnnotation> isInclu
163167
}
164168

165169
if (isIncluded.test(subtree)) {
166-
chunksToWrite.addAll(subtree.getAllLinesFor(isIncluded));
170+
chunksToWrite.add(subtree.getLinesToCopy(isIncluded));
167171
}
168172

169173
currentLine = subtree.getLineTo() + 1;
@@ -173,7 +177,10 @@ public List<Integer> getAllLinesFor(final Predicate<LineBasedAnnotation> isInclu
173177
addRange(chunksToWrite, currentLine, lastCodeLine);
174178
}
175179

176-
return chunksToWrite;
180+
return new VariantAnnotation(
181+
this.getFeatureMapping(),
182+
chunksToWrite
183+
);
177184
}
178185

179186
public void simplify() {

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.append(
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: 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+
}

0 commit comments

Comments
 (0)