Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
207aa53
delete deprecated ViewTest
pmbittner Oct 13, 2025
ed7ff62
feat: Label::observablyEqual
pmbittner Oct 13, 2025
dedd98b
feat: DiffNode::isSameAsIgnoringLineNumbers
pmbittner Oct 13, 2025
312ff2e
feat: VariationDiff:isSameAsIgnoringLineNumbers
pmbittner Oct 13, 2025
66e0970
fix: some typos in VariationTreeNode documentation
pmbittner Oct 13, 2025
99f9df5
feat: specification for Configure
pmbittner Oct 13, 2025
a51ad75
docs: refine documentation on Relevance::computeViewNodesCheckAll
pmbittner Oct 13, 2025
9f2c50d
fix: Configure falsely removed ELSE and ELIF nodes
pmbittner Oct 13, 2025
48f93b4
test: new tests for Views
pmbittner Oct 13, 2025
8c63c20
refactor: extract Transformer super interface ...
pmbittner Oct 20, 2025
9dfbfb2
VariationTreeTransformer interface
pmbittner Oct 20, 2025
fa62e30
VariationTreeNode::setFormula
pmbittner Oct 20, 2025
6b2bcb0
feat: traversing variation trees in post-order
pmbittner Oct 20, 2025
a48b4fd
feat: DiffNode::stealChildrenOf at time
pmbittner Oct 20, 2025
fd0d96e
fix: VariationNode::stealChildrenOf
pmbittner Oct 20, 2025
1939f50
feat: EliminateEmptyAlternatives transformer
pmbittner Oct 20, 2025
7f70843
fix: javadoc error in Transformer.java
pmbittner Oct 20, 2025
db2fccf
fix: javadoc error in EliminateEmptyAlternatives
pmbittner Oct 20, 2025
7daeff0
fix: VariationNode::stealChildrenOf once again
pmbittner Oct 24, 2025
2c6d651
Simplify Assertion in Configure
pmbittner Oct 26, 2025
8197880
remove boilerplate Variation(Tree|Diff)Transformer
pmbittner Oct 26, 2025
7e0365b
DiffNode::makeUnchanged()
pmbittner Oct 27, 2025
ddd4289
feat: HideSomeChanges
pmbittner Oct 27, 2025
551a1b6
fix: bug + alignment in DiffNode::makeUnchanged
pmbittner Oct 28, 2025
02804e5
feat: IndexFormat
pmbittner Oct 28, 2025
6a2c992
support IndexFormat in GUI
pmbittner Oct 28, 2025
75be10f
StringUtils::getLeadingWhitespace
pmbittner Nov 3, 2025
1d227f5
fix: EliminateEmptyAlt. making inconsistent labels
pmbittner Nov 3, 2025
02ae9a3
Merge branch 'develop' into fix-configure
pmbittner Nov 5, 2025
a991942
fix: ConfigureSpec vs new Source interface
pmbittner Nov 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@
import java.util.Arrays;
import java.util.List;

import org.variantsync.diffdetective.variation.diff.transform.VariationDiffTransformer;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.transform.Transformer;
import org.variantsync.diffdetective.variation.DiffLinesLabel;

public class PreprocessingAnalysis implements Analysis.Hooks {
private final List<VariationDiffTransformer<DiffLinesLabel>> preprocessors;
private final List<Transformer<VariationDiff<DiffLinesLabel>>> preprocessors;

public PreprocessingAnalysis(List<VariationDiffTransformer<DiffLinesLabel>> preprocessors) {
public PreprocessingAnalysis(List<Transformer<VariationDiff<DiffLinesLabel>>> preprocessors) {
this.preprocessors = preprocessors;
}

@SafeVarargs
public PreprocessingAnalysis(VariationDiffTransformer<DiffLinesLabel>... preprocessors) {
public PreprocessingAnalysis(Transformer<VariationDiff<DiffLinesLabel>>... preprocessors) {
this.preprocessors = Arrays.asList(preprocessors);
}

@Override
public boolean analyzeVariationDiff(Analysis analysis) {
VariationDiffTransformer.apply(preprocessors, analysis.getCurrentVariationDiff());
Transformer.apply(preprocessors, analysis.getCurrentVariationDiff());
analysis.getCurrentVariationDiff().assertConsistency();
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import org.variantsync.diffdetective.variation.diff.render.RenderOptions;
import org.variantsync.diffdetective.variation.diff.render.VariationDiffRenderer;
import org.variantsync.diffdetective.variation.diff.serialize.nodeformat.MappingsDiffNodeFormat;
import org.variantsync.diffdetective.variation.diff.transform.VariationDiffTransformer;
import org.variantsync.diffdetective.variation.diff.transform.Transformer;

import java.io.IOException;
import java.nio.file.Files;
Expand Down Expand Up @@ -162,10 +162,10 @@ public static void main(String[] args) throws IOException {
final Repository repository = Repository.fromDirectory(repoPath, repoName);
repository.setParseOptions(repository.getParseOptions().withDiffStoragePolicy(PatchDiffParseOptions.DiffStoragePolicy.REMEMBER_STRIPPED_DIFF));

final List<VariationDiffTransformer<DiffLinesLabel>> transform = VariationDiffMiner.Postprocessing(repository);
final List<Transformer<VariationDiff<DiffLinesLabel>>> transform = VariationDiffMiner.Postprocessing(repository);
final PatchDiff patch = VariationDiffParser.parsePatch(repository, file, commit);
Assert.assertNotNull(patch != null);
VariationDiffTransformer.apply(transform, patch.getVariationDiff());
Transformer.apply(transform, patch.getVariationDiff());
renderer.render(patch, Path.of("render", repoName), RENDER_OPTIONS_TO_USE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
import org.variantsync.diffdetective.mining.formats.MiningNodeFormat;
import org.variantsync.diffdetective.mining.formats.ReleaseMiningDiffNodeFormat;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.filter.VariationDiffFilter;
import org.variantsync.diffdetective.variation.diff.serialize.GraphFormat;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphExportOptions;
import org.variantsync.diffdetective.variation.diff.serialize.edgeformat.EdgeLabelFormat;
import org.variantsync.diffdetective.variation.diff.serialize.treeformat.CommitDiffVariationDiffLabelFormat;
import org.variantsync.diffdetective.variation.diff.transform.CollapseNestedNonEditedAnnotations;
import org.variantsync.diffdetective.variation.diff.transform.CutNonEditedSubtrees;
import org.variantsync.diffdetective.variation.diff.transform.VariationDiffTransformer;
import org.variantsync.diffdetective.variation.diff.transform.Transformer;

import java.io.IOException;
import java.nio.file.Path;
Expand All @@ -38,8 +39,8 @@ public class VariationDiffMiner {
// public static final int PRINT_LARGEST_SUBJECTS = 3;
public static final boolean DEBUG_TEST = false;

public static List<VariationDiffTransformer<DiffLinesLabel>> Postprocessing(final Repository repository) {
final List<VariationDiffTransformer<DiffLinesLabel>> processing = new ArrayList<>();
public static List<Transformer<VariationDiff<DiffLinesLabel>>> Postprocessing(final Repository repository) {
final List<Transformer<VariationDiff<DiffLinesLabel>>> processing = new ArrayList<>();
processing.add(new CutNonEditedSubtrees<>());
processing.add(new CollapseNestedNonEditedAnnotations());
return processing;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.variantsync.diffdetective.variation.diff.filter.ExplainedFilter;
import org.variantsync.diffdetective.variation.diff.filter.TaggedPredicate;
import org.variantsync.diffdetective.variation.diff.transform.CutNonEditedSubtrees;
import org.variantsync.diffdetective.variation.diff.transform.VariationDiffTransformer;
import org.variantsync.diffdetective.variation.diff.transform.Transformer;

import java.util.List;
import java.util.Map;
Expand All @@ -17,7 +17,7 @@
* Patterns are represented as VariationDiffs and might be filtered or transformed.
*/
public class Postprocessor<L extends Label> {
private final List<VariationDiffTransformer<L>> transformers;
private final List<Transformer<VariationDiff<L>>> transformers;
private final ExplainedFilter<VariationDiff<L>> filters;

/**
Expand All @@ -30,7 +30,7 @@ public class Postprocessor<L extends Label> {
public record Result<L extends Label>(List<VariationDiff<L>> processedTrees, Map<String, Integer> filterCounts) {}

private Postprocessor(
final List<VariationDiffTransformer<L>> transformers,
final List<Transformer<VariationDiff<L>>> transformers,
final List<TaggedPredicate<String, ? super VariationDiff<L>>> namedFilters) {
this.transformers = transformers;
this.filters = new ExplainedFilter<VariationDiff<L>>(namedFilters.stream());
Expand Down Expand Up @@ -66,7 +66,7 @@ public static <L extends Label> Postprocessor<L> Default() {
public Result<L> postprocess(final List<VariationDiff<L>> frequentSubgraphs) {
final List<VariationDiff<L>> processedTrees = frequentSubgraphs.stream()
.filter(filters)
.peek(tree -> VariationDiffTransformer.apply(transformers, tree))
.peek(tree -> Transformer.apply(transformers, tree))
.toList();

final Map<String, Integer> filterCounts = new ExplainedFilterSummary(filters).snapshot();
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/variantsync/diffdetective/variation/Label.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,18 @@ default Label withTimeDependentStateFrom(Label other, Time time) {
* Creates a deep copy of this label.
*/
Label clone();

/**
* Tests whether two labels are observably equal.
* Observably equal means that the two labels
* cannot be distinguished by using methods from this interface.
* The given labels might indeed be of different classes or might
* be considered unequal regarding {@link Object#equals(Object)}.
*/
static boolean observablyEqual(Label a, Label b) {
if (a == null) return false;
if (a == b) return true;
return a.getLines().equals(b.getLines())
&& a.getTrailingLines().equals(b.getTrailingLines());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,21 @@ public List<DiffNode<L>> removeChildren(Time time) {
}

/**
* Removes all children from the given node and adds them as children to this node at the respective times.
* Removes all children from the given node and adds them as children to this node at the given time.
* The given node will have no children afterwards at the given time.
* @param other The node whose children should be stolen for the given time.
*/
public void stealChildrenOf(Time time, final DiffNode<L> other) {
addChildren(other.removeChildren(time), time);
}

/**
* Removes all children from the given node and adds them as children to this node (at all times).
* The given node will have no children afterwards.
* @param other The node whose children should be stolen.
*/
public void stealChildrenOf(final DiffNode<L> other) {
Time.forAll(time -> addChildren(other.removeChildren(time), time));
Time.forAll(time -> stealChildrenOf(time, other));
}

/**
Expand Down Expand Up @@ -924,6 +933,42 @@ private static <L extends Label> boolean isSameAs(DiffNode<L> a, DiffNode<L> b,
return aIt.hasNext() == bIt.hasNext();
}

/**
* Returns true if this subtree is exactly equal to {@code other} except for line numbers and other metadata in labels.
* This equality is a weaker equality than {@link DiffNode#isSameAs(DiffNode)} (i.e., whenever isSameAs returns true, so does
* isSameAsIgnoringLineNumbers).
* Labels of DiffNodes are compared via {@link Label#observablyEqual(Label, Label)}.
* This check uses equality checks instead of identity.
*/
public boolean isSameAsIgnoringLineNumbers(DiffNode<L> other) {
return isSameAsIgnoringLineNumbers(this, other, new HashSet<>());
}

private static <L extends Label> boolean isSameAsIgnoringLineNumbers(DiffNode<L> a, DiffNode<L> b, Set<DiffNode<L>> visited) {
if (!visited.add(a)) {
return true;
}

if (!(
a.getDiffType().equals(b.getDiffType()) &&
a.getNodeType().equals(b.getNodeType()) &&
Objects.equals(a.getFormula(), b.getFormula()) &&
Label.observablyEqual(a.getLabel(), b.getLabel())
)) {
return false;
}

Iterator<DiffNode<L>> aIt = a.getAllChildren().iterator();
Iterator<DiffNode<L>> bIt = b.getAllChildren().iterator();
while (aIt.hasNext() && bIt.hasNext()) {
if (!isSameAsIgnoringLineNumbers(aIt.next(), bIt.next(), visited)) {
return false;
}
}

return aIt.hasNext() == bIt.hasNext();
}

@Override
public String toString() {
String s;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,10 +535,20 @@ public ConsistencyResult isConsistent() {
return ConsistencyResult.Success();
}

/**
* @see DiffNode#isSameAs
*/
public boolean isSameAs(VariationDiff<L> b) {
return getRoot().isSameAs(b.getRoot());
}

/**
* @see DiffNode#isSameAsIgnoringLineNumbers
*/
public boolean isSameAsIgnoringLineNumbers(VariationDiff<L> b) {
return getRoot().isSameAsIgnoringLineNumbers(b.getRoot());
}

@Override
public String toString() {
return "VariationDiff of " + source;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
*
* @author Paul Bittner
*/
public class CollapseNestedNonEditedAnnotations implements VariationDiffTransformer<DiffLinesLabel> {
public class CollapseNestedNonEditedAnnotations implements Transformer<VariationDiff<DiffLinesLabel>> {
private final List<Stack<DiffNode<DiffLinesLabel>>> chainCandidates = new ArrayList<>();
private final List<Stack<DiffNode<DiffLinesLabel>>> chains = new ArrayList<>();

@Override
public List<Class<? extends VariationDiffTransformer<DiffLinesLabel>>> getDependencies() {
public List<Class<? extends Transformer<VariationDiff<DiffLinesLabel>>>> getDependencies() {
return List.of(Cast.unchecked(CutNonEditedSubtrees.class));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* of our edit classes in our ESEC/FSE'22 paper.
* @author Paul Bittner
*/
public class CutNonEditedSubtrees<L extends Label> implements VariationDiffTransformer<L>, VariationDiffVisitor<L> {
public class CutNonEditedSubtrees<L extends Label> implements Transformer<VariationDiff<L>>, VariationDiffVisitor<L> {
private final boolean keepDummy;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

package org.variantsync.diffdetective.variation.diff.transform;

import org.prop4j.Node;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.tree.VariationTree;
import org.variantsync.diffdetective.variation.tree.VariationTreeNode;

import java.util.List;

import static org.variantsync.diffdetective.util.fide.FormulaUtils.*;

/**
* This transformer simplifies annotations such that empty alternatives do not appear in choices.
* This means, that nestings without any siblings such as
*
* <pre>{@code
* #if A
* #elif B
* #elif C
* #else
* foo
* #endif
* }</pre>
*
* will be simplified to
*
* <pre>{@code
* #if !A && !B && !C
* foo
* #endif
* }</pre>
*
* Annotations without any children also get eliminated.
*
* @author Paul Bittner
*/
public class EliminateEmptyAlternatives<L extends Label> implements Transformer<VariationTree<L>> {
private void elim(VariationTreeNode<L> subtree) {
// We simplify only annotations.
if (!subtree.isAnnotation()) return;

final List<VariationTreeNode<L>> children = subtree.getChildren();

// When there are no children, 'subtree' is an empty annotation that can be eliminated.
if (children.isEmpty()) {
subtree.drop();
}
// When there is exactly one child and that child is an 'else' or 'elif' we can simplify that nesting.
else if (children.size() == 1) {
final VariationTreeNode<L> child = children.getFirst();

if ((subtree.isIf() || subtree.isElif()) && (child.isElif() || child.isElse())) {
// determine new feaure mapping
Node newFormula = negate(subtree.getFormula());
if (child.isElif()) {
newFormula = and(newFormula, child.getFormula());
}
subtree.setFormula(newFormula);

// simplify tree
child.drop();
subtree.stealChildrenOf(child);
}
}
}

@Override
public void transform(VariationTree<L> tree) {
tree.forAllPostorder(this::elim);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* For example, it might remove an IF but keep its ELSE branches which is illegal.
*/
@Deprecated
public record FeatureExpressionFilter<L extends Label>(Predicate<DiffNode<L>> isFeatureAnnotation) implements VariationDiffTransformer<L> {
public record FeatureExpressionFilter<L extends Label>(Predicate<DiffNode<L>> isFeatureAnnotation) implements Transformer<VariationDiff<L>> {
@Override
public void transform(VariationDiff<L> variationDiff) {
final List<DiffNode<L>> illegalNodes = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
* All other nodes will be labeled by the {@link NodeType#name name of their node type}.
* @author Paul Bittner
*/
public class LabelWithEditClass implements VariationDiffTransformer<DiffLinesLabel> {
private final VariationDiffTransformer<DiffLinesLabel> relabelNodes;
public class LabelWithEditClass implements Transformer<VariationDiff<DiffLinesLabel>> {
private final Transformer<VariationDiff<DiffLinesLabel>> relabelNodes;

/**
* Creates a new transformation that will use the given catalog of edit classes
Expand All @@ -39,7 +39,7 @@ public void transform(VariationDiff<DiffLinesLabel> variationDiff) {
}

@Override
public List<Class<? extends VariationDiffTransformer<DiffLinesLabel>>> getDependencies() {
public List<Class<? extends Transformer<VariationDiff<DiffLinesLabel>>>> getDependencies() {
return relabelNodes.getDependencies();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* but for each line in the nodes individually.
* @author Paul Bittner
*/
public class NaiveMovedArtifactDetection<L extends Label> implements VariationDiffTransformer<L> {
public class NaiveMovedArtifactDetection<L extends Label> implements Transformer<VariationDiff<L>> {
@Override
public void transform(final VariationDiff<L> variationDiff) {
final List<Pair<DiffNode<L>, DiffNode<L>>> twins = findArtifactTwins(variationDiff);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Transformer that changes the label of each node using a relable function.
* @author Paul Bittner
*/
public class RelabelNodes<L extends Label> implements VariationDiffTransformer<L> {
public class RelabelNodes<L extends Label> implements Transformer<VariationDiff<L>> {
private final Function<DiffNode<L>, L> getLabel;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Transformer that relabels the root of a VariationDiff.
* @author Paul Bittner
*/
public class RelabelRoot<L extends Label> implements VariationDiffTransformer<L> {
public class RelabelRoot<L extends Label> implements Transformer<VariationDiff<L>> {
private final L newLabel;

/**
Expand Down
Loading