Skip to content

Commit 2762ab9

Browse files
authored
Merge pull request #173 from VariantSync/variant-generation
Some little features
2 parents 467a523 + f3675f0 commit 2762ab9

7 files changed

Lines changed: 131 additions & 6 deletions

File tree

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.variantsync.diffdetective.variation.diff.traverse.VariationDiffTraversal;
2525
import org.variantsync.diffdetective.variation.diff.traverse.VariationDiffVisitor;
2626
import org.variantsync.diffdetective.variation.tree.VariationTree;
27+
import org.variantsync.diffdetective.util.fide.FixTrueFalse;
2728
import org.variantsync.functjonal.Cast;
2829
import org.variantsync.functjonal.Result;
2930

@@ -34,6 +35,7 @@
3435
import java.nio.file.Path;
3536
import java.util.ArrayList;
3637
import java.util.HashMap;
38+
import java.util.LinkedHashSet;
3739
import java.util.List;
3840
import java.util.Map;
3941
import java.util.concurrent.atomic.AtomicBoolean;
@@ -207,7 +209,7 @@ public static <L extends Label> VariationDiff<L> fromTrees(VariationTree<L> befo
207209
* The returned value is a deep copy of the variation tree within this diff
208210
* at the given time.
209211
* If you instead wish to only have a view on the tree at the given diff
210-
* have a look at {@link DiffNode#projection(Time)} for this trees {@link #getRoot() root}.
212+
* have a look at {@link DiffNode#projection(Time)} for this tree's {@link #getRoot() root}.
211213
* @param t The time for which to project the variation tree.
212214
*/
213215
public VariationTree<L> project(Time t) {
@@ -374,6 +376,24 @@ public int count(final Predicate<DiffNode<L>> nodesToCount) {
374376
return count.get();
375377
}
376378

379+
/**
380+
* Returns all variable names occurring in annotations (i.e., formulas of mapping nodes) in this variation diff.
381+
* This method is deterministic: It will return the feature names always in the same order, assuming the variation diff is not changed inbetween.
382+
* @return A set of every occuring feature name.
383+
*/
384+
public LinkedHashSet<String> computeAllFeatureNames() {
385+
LinkedHashSet<String> features = new LinkedHashSet<>();
386+
forAll(node -> {
387+
if (node.isConditionalAnnotation()) {
388+
features.addAll(node.getFormula().getUniqueContainedFeatures());
389+
}
390+
});
391+
// Since FeatureIDE falsely reports constants "True" and "False" as feature names, we have to remove them from the resulting set.
392+
features.removeIf(FixTrueFalse::isTrueLiteral);
393+
features.removeIf(FixTrueFalse::isFalseLiteral);
394+
return features;
395+
}
396+
377397
/**
378398
* Sets the source of this VariationDiff.
379399
* @see VariationDiffSource

src/main/java/org/variantsync/diffdetective/variation/tree/view/TreeView.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ public static <L extends Label> void treeInline(final VariationTree<L> t, final
6161
/**
6262
* Creates a view on the given variation tree as described by the given relevance predicate.
6363
* This function is side-effect free.
64-
* Thre given variation tree and relevance will not be altered.
64+
* The given variation tree and relevance will not be altered.
6565
* This function corresponds to Equation 4 in our SPLC'23 paper - Views on Edits to Variational Software.
6666
* @param t The variation tree to generate a view on.
6767
* @param r A relevance predicate that determines for each node in the
6868
* tree whether it should be contained in the view or should be excluded.
6969
* @return A variation tree that represents a view on the given variation tree t.
7070
*/
71-
public static VariationTree<?> tree(final VariationTree<?> t, final Relevance r) {
72-
final VariationTree<?> copy = t.deepCopy();
71+
public static <L extends Label> VariationTree<L> tree(final VariationTree<L> t, final Relevance r) {
72+
final VariationTree<L> copy = t.deepCopy();
7373
treeInline(copy, r);
7474
return copy;
7575
}

src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Configure.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,26 @@
44
import org.prop4j.NodeWriter;
55
import org.variantsync.diffdetective.analysis.logic.SAT;
66
import org.variantsync.diffdetective.util.fide.FixTrueFalse;
7+
import org.variantsync.diffdetective.util.fide.FixTrueFalse.Formula;
78
import org.variantsync.diffdetective.variation.tree.VariationNode;
89

10+
import java.util.Map;
11+
import java.util.Map.Entry;
912
import java.util.function.Consumer;
1013

1114
/**
1215
* Relevance predicate that generates (partial) variants from variation trees.
1316
* This relevance predicate is the implementation of Equation 5 in our SPLC'23 paper.
1417
*/
1518
public class Configure implements Relevance {
16-
private final FixTrueFalse.Formula configuration;
19+
private final Formula configuration;
1720

1821
/**
1922
* Same as {@link Configure#Configure(Node)} but with a formula that is witnessed to
2023
* not contain true or false constants not at the root.
2124
* Workaround for FeatureIDE bug <a href="https://github.com/FeatureIDE/FeatureIDE/issues/1333">FeatureIDE Issue 1333</a>.
2225
*/
23-
public Configure(final FixTrueFalse.Formula configuration) {
26+
public Configure(final Formula configuration) {
2427
this.configuration = configuration;
2528
}
2629

@@ -35,6 +38,39 @@ public Configure(final Node configuration) {
3538
this(FixTrueFalse.EliminateTrueAndFalse(configuration));
3639
}
3740

41+
/**
42+
* Create a configuration from an assignment of variable names to boolean values.
43+
* The given assignment may be complete or partial.
44+
* Internally, a big conjunction of literals is created:
45+
* <pre>
46+
* ⋀ f ∧ ⋀ ¬ f
47+
* (f, true) ∈ assignment (f, false) ∈ assignment
48+
* </pre>
49+
*
50+
* As an example, suppose the map contains the following entries:
51+
* <pre>
52+
* A ↦ true
53+
* B ↦ false
54+
* C ↦ true
55+
* </pre>
56+
* then we construct a formula A ∧ (¬ B) ∧ C.
57+
*/
58+
public Configure(final Map<String, Boolean> assignment) {
59+
// We use commutativity of ∧ to iterate the map only once instead of twice as shown in the formula above.
60+
final Formula[] fixedFeatures = new Formula[assignment.size()];
61+
int i = 0;
62+
for (Entry<String, Boolean> entry : assignment.entrySet()) {
63+
fixedFeatures[i] = Formula.var(entry.getKey());
64+
if (!entry.getValue()) {
65+
fixedFeatures[i] = Formula.not(fixedFeatures[i]);
66+
}
67+
68+
++i;
69+
}
70+
71+
this.configuration = Formula.and(fixedFeatures);
72+
}
73+
3874
@Override
3975
public boolean test(VariationNode<?, ?> v) {
4076
return SAT.isSatisfiable(
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import static org.junit.jupiter.api.Assertions.assertEquals;
2+
3+
import java.io.IOException;
4+
import java.nio.file.Path;
5+
import java.util.LinkedHashSet;
6+
import java.util.List;
7+
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.MethodSource;
10+
import org.variantsync.diffdetective.diff.result.DiffParseException;
11+
import org.variantsync.diffdetective.variation.DiffLinesLabel;
12+
import org.variantsync.diffdetective.variation.Label;
13+
import org.variantsync.diffdetective.variation.diff.VariationDiff;
14+
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
15+
16+
/**
17+
* Test for {@link VariationDiff#computeAllFeatureNames()}.
18+
*/
19+
public class FeatureNamesTest {
20+
private final static Path
21+
diffsDir = Constants.RESOURCE_DIR.resolve("diffs"),
22+
pctestDir = Constants.RESOURCE_DIR.resolve("pctest"),
23+
ourDir = Constants.RESOURCE_DIR.resolve("featurenames"),
24+
collapseDir = diffsDir.resolve("collapse"),
25+
moveDir = diffsDir.resolve("move");
26+
27+
public record TestCase<L extends Label>(String origin, VariationDiff<L> diff, LinkedHashSet<String> features) {
28+
public static TestCase<DiffLinesLabel> fromFile(Path p, String... features) throws IOException, DiffParseException {
29+
return new TestCase<>(
30+
p.toString(),
31+
VariationDiff.fromFile(p, VariationDiffParseOptions.Default),
32+
new LinkedHashSet<String>(List.of(features))
33+
);
34+
}
35+
};
36+
37+
private static List<TestCase<DiffLinesLabel>> diffFeatureNameTestCases() throws IOException, DiffParseException {
38+
return List.of(
39+
TestCase.fromFile(collapseDir.resolve("elif.txt"), "A", "B", "C", "D", "E"),
40+
TestCase.fromFile(collapseDir.resolve("simple.txt"), "A", "B", "C"),
41+
TestCase.fromFile(moveDir.resolve("simple.txt"), "X", "Y"),
42+
TestCase.fromFile(pctestDir.resolve("a.diff"), "A", "D", "E", "B", "C"),
43+
TestCase.fromFile(pctestDir.resolve("elif.diff"), "A", "B", "C", "D"),
44+
TestCase.fromFile(pctestDir.resolve("else.diff"), "A", "C", "B"),
45+
TestCase.fromFile(ourDir.resolve("empty.diff")),
46+
TestCase.fromFile(ourDir.resolve("a.diff"), "FIRST", "SECOND"),
47+
TestCase.fromFile(ourDir.resolve("b.diff"), "A")
48+
);
49+
}
50+
51+
@ParameterizedTest
52+
@MethodSource("diffFeatureNameTestCases")
53+
public void testComputeAllFeatureNames(TestCase<DiffLinesLabel> testCase) {
54+
assertEquals(testCase.features(), testCase.diff().computeAllFeatureNames(), testCase.origin());
55+
}
56+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#if FIRST
2+
foo
3+
#if SECOND
4+
bar
5+
#endif
6+
#endif
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-#if A
2+
foo
3+
#if A
4+
bar
5+
#endif
6+
-#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo

0 commit comments

Comments
 (0)