getSourceArguments() {
+ return List.of(getPath());
+ }
+}
diff --git a/src/main/java/org/variantsync/diffdetective/util/Source.java b/src/main/java/org/variantsync/diffdetective/util/Source.java
new file mode 100644
index 000000000..a2141ec1c
--- /dev/null
+++ b/src/main/java/org/variantsync/diffdetective/util/Source.java
@@ -0,0 +1,275 @@
+package org.variantsync.diffdetective.util;
+
+import java.nio.file.Path; // For Javadoc
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.variantsync.diffdetective.diff.git.PatchDiff; // For Javadoc
+import org.variantsync.diffdetective.variation.diff.ProjectionSource;
+import org.variantsync.diffdetective.variation.diff.VariationDiff; // For Javadoc
+import org.variantsync.functjonal.Cast;
+
+/**
+ * An interface for source traceability.
+ * For testing and debugging, it can be really useful to know where some value originated from. For
+ * example, imagine an error during you analysis of a {@link VariationDiff}. Now you want to know
+ * where the variation diff came from. The solution: DiffDetectice tracks the source and
+ * transformations of important data structures like {@link VariationDiff#getSource()} and allows
+ * you to inspect them using this interface.
+ *
+ * {@link Source} represents a hierarchy of inputs, processing steps, and results (the distinction
+ * between these types is not directly reflected in the API and only serve as explanation of the
+ * interface).
+ *
+ * An input (e.g., {@link FileSource}) is a {@link Source} that has no {@link getSources
+ * children}. Inputs are the leafs of the {@link Source} hierarchy and contain the {@link
+ * getSourceExplanation name of the input} (e.g., {@code "file"} or {@code "URL"}) and {@link
+ * getSourceArguments some arguments} (e.g., the file name or URL).
+ * A step (e.g., {@link ProjectionSource}) processed {@link getSources one or more sources}, has
+ * a {@link getSourceExplanation name} and may be configurable by some {@link getSourceArguments
+ * arguments}. Unconfigurable processing steps (or steps whose configurability should not be
+ * tracked) are most commonly represented by {@link CompositeSource}. Note that a step might also
+ * include (interim) results and might thus be considered a result too.
+ * A result (e.g., {@link VariationDiff}) contains some data and the {@link getSources source of
+ * that data}. The contained data is not directly accessible by {@link Source this interface} but
+ * can be obtained by looking up the subclass representing that result with {@link findFirst} or
+ * {@link findAll}. Results are not necessary to trace the source of some data (and it might thus be
+ * omitted from the {@link Source} hierarchy) but can help to identify a buggy processing step.
+ *
+ *
+ * In summary, each {@link Source} in the {@link getSources hierarchy} has a {@link
+ * getSourceExplanation short explanation} and a number of {@link getSourceArguments arguments}. A
+ * source may contain additional data (e.g., the diff a variation diff originated from) that is too
+ * big to be printed by the standard formatting functions ({@link shallowExplanation}, {@link
+ * shortExplanation}, {@link fullExplanation}) of this interface. To access such data, or the
+ * arguments of specific subclasses, you can use {@link findFirst} and {@link findAll}.
+ *
+ * For dealing with generic sources, it is recommended to use the static methods of this interface.
+ * They provide a more readable interface (e.g., {@code variationDiff.shallowExplanation()} vs.
+ * {@code Source.shallowExplanation(variationDiff)}) and correctly deal with {@code null} sources
+ * (treated as {@link Unknown}).
+ *
+ * @see CompositeSource
+ * @see FileSource
+ */
+public interface Source {
+ /**
+ * Returns a short, one line explanation or identifier of this source.
+ * The result is used by {@link shallowExplanation} and is formatted together with {@link
+ * getSourceArguments} like {@code "sourceExplanation(argument1, argument2)"}.
+ */
+ String getSourceExplanation();
+
+ /**
+ * Returns a list of arguments required to understand this source.
+ * Each argument in the result list should represent a {@link Object#toString string} without
+ * newlines and will be formatted together with {@link getSourceExplanation} like {@code
+ * "sourceExplanation(argument1, argument2)"} by {@link shallowExplanation}. This method is only
+ * intended to access the arguments for {@link Object#toString printing}. In case access to the
+ * well-typed object is required, use {@link findFirst} or {@link findAll} and use the accessors
+ * of the underlying type.
+ *
+ * For ease of implementing {@code Source}, the return value is a list of {@link Object}s
+ * instead of a list of {@link String}s to allow code like {@code List.of(arg0, arg1, arg2)}
+ * instead of {@code List.of(arg0.toString(), arg1.toString(), arg2.toString())}. Users of this
+ * function should assume nothing from the returned objects except that {@link Object#toString}
+ * is well behaved.
+ *
+ * @return an empty list by default
+ * @see shallowExplanation
+ */
+ default List getSourceArguments() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns a list of sources that influenced this source.
+ * Noteworthy processing steps and incorporation of multiple sources are implemented as a tree
+ * of sources. This functions returns the children of this source.
+ *
+ * By default, the first child source is treated specially in {@link getRootSource}.
+ *
+ * @return an empty list by default
+ */
+ default List getSources() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * An explanation of this source disregarding all {@link getSources child sources}.
+ * The resulting string should not contain any newlines.
+ *
+ * @return {@code "sourceExplanation(argument1, argument2, ...)"} by default
+ * @see getSourceExplanation
+ * @see getSourceArguments
+ */
+ default String shallowExplanation() {
+ return getSourceArguments()
+ .stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", ", getSourceExplanation() + "(", ")"));
+ }
+
+ /**
+ * Calls {@link shallowExplanation} on {@code source} but handles {@code null} like {@link
+ * Unknown}.
+ */
+ static String shallowExplanation(Source source) {
+ return (source == null ? Unknown : source).shallowExplanation();
+ }
+
+ /**
+ * Returns one representative {@link Source} that identifies the initial data.
+ * A good root source should help humans to understand at what data they are looking at. Good
+ * examples is a filename or a commit and repository combination. Note that sometimes this is an
+ * arbitrary decision (e.g., the state before or after a diff) and might depend on the problem
+ * that is currently worked on. Hence, there is might not be one correct result in all
+ * circumstances.
+ *
+ * @return the {@link getSources first child} or {@code this} if there are no {@link getSources children}
+ */
+ default Source getRootSource() {
+ for (var source : getSources()) {
+ if (source != null) {
+ return source.getRootSource();
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Returns a {@link shallowExplanation} of {@link getRootSource the root source} of {@code source}.
+ */
+ static String rootExplanation(Source source) {
+ return (source == null ? Unknown : source.getRootSource())
+ .shallowExplanation();
+ }
+
+ /**
+ * Returns a short one line explanation of {@code source} by pairing {@link shortExplanation}
+ * with {@link rootExplanation}.
+ */
+ static String shortExplanation(Source source) {
+ if (source == null) {
+ return Unknown.shallowExplanation();
+ }
+
+ Source root = source.getRootSource();
+ if (root == source) {
+ return source.shallowExplanation();
+ } else {
+ return source.shallowExplanation() + " of " + root.shallowExplanation();
+ }
+ }
+
+ /**
+ * Explains {@code this} source hierarchy in detail.
+ * The {@code result} will contain at least one line per {@link Source} in the {@link getSources
+ * hierarchy} (by default in the {@link shallowExplanation} format). The state of {@code result}
+ * must be in a new line before and after a call to this method.
+ *
+ * @param level the current indentation level (two spaces per level) that needs to be added
+ * before each line
+ * @param result the string containing the result of this method
+ */
+ default void fullExplanation(int level, StringBuilder result) {
+ for (int i = 0; i < level; ++i) {
+ result.append(" ");
+ }
+ result.append(shallowExplanation(this));
+ result.append("\n");
+
+ for (var child : getSources()) {
+ if (child == null) {
+ Unknown.fullExplanation(level + 1, result);
+ } else {
+ child.fullExplanation(level + 1, result);
+ }
+ }
+ }
+
+ /**
+ * Calls {@link fullExplanation(int, StringBuilder)} on {@code source} but handles {@code null}
+ * like {@link Unknown} and returns the resulting string.
+ */
+ static String fullExplanation(Source source) {
+ if (source == null) {
+ return fullExplanation(Unknown);
+ }
+
+ var result = new StringBuilder();
+ source.fullExplanation(0, result);
+ return result.toString();
+ }
+
+ /**
+ * Uses a depth first traversal of the {@link Source} {@link getSources hierarchy} to find and
+ * return all instances of {@code clazz}.
+ * This method is intended to provide access to the data contained in the source hierarchy. For
+ * example, {@code Source.findAll(src, File.class)} finds all source files. Note that arguments
+ * are not inspected. In case an implementation {@link Source} contains a {@link Path file} as
+ * {@link getSourceArguments argument} it is not included in the result.
+ *
+ * @see findFirst
+ */
+ static List findAll(Source source, Class clazz) {
+ var result = new ArrayList();
+ findAll(result, source, clazz);
+ return result;
+ }
+
+ /**
+ * Implementation of {@link findAll(Source, Class)} using an {@code result} accumulator.
+ */
+ private static void findAll(List result, Source source, Class clazz) {
+ if (source == null) {
+ return;
+ }
+
+ if (source.getClass() == clazz) {
+ result.add(Cast.unchecked(source));
+ }
+
+ for (var child : source.getSources()) {
+ findAll(result, child, clazz);
+ }
+ }
+
+ /**
+ * Uses a depth first traversal of the {@link Source} {@link getSources hierarchy} to find and
+ * return the first instances of {@code clazz}.
+ * This method is intended to provide access to the data contained in the source hierarchy. For
+ * example, {@code Source.findFirst(src, PatchDiff.class)} finds the first {@link PatchDiff}.
+ * This allows access to data and arguments like {@link PatchDiff#getDiff} but it does not
+ * traverse the {@link getSourceArguments} directly.
+ *
+ * @see findAll
+ */
+ static T findFirst(Source source, Class clazz) {
+ if (source == null) {
+ return null;
+ }
+
+ if (source.getClass() == clazz) {
+ return Cast.unchecked(source);
+ }
+
+ for (var child : source.getSources()) {
+ var result = findFirst(child, clazz);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * A placeholder for an unknown source without any explanation.
+ * Should be preferred to a {@code null} {@link Source} to make it easily grepable.
+ */
+ public static Source Unknown = new CompositeSource("Unknown");
+}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffGraph.java b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffGraph.java
index df1d7e6e6..24da64f76 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/DiffGraph.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/DiffGraph.java
@@ -1,7 +1,7 @@
package org.variantsync.diffdetective.variation.diff;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import java.util.Collection;
@@ -20,10 +20,10 @@ public final class DiffGraph {
private DiffGraph() {}
/**
- * Invokes {@link DiffGraph#fromNodes(Collection, VariationDiffSource)} )} with an unknown VariationDiffSource.
+ * Invokes {@link DiffGraph#fromNodes(Collection, Source)} )} with an unknown source.
*/
public static VariationDiff fromNodes(final Collection> nodes) {
- return fromNodes(nodes, VariationDiffSource.Unknown);
+ return fromNodes(nodes, Source.Unknown);
}
/**
@@ -33,7 +33,7 @@ public static VariationDiff fromNodes(final Collection fromNodes(final Collection> nodes, final VariationDiffSource source) {
+ public static VariationDiff fromNodes(final Collection> nodes, final Source source) {
final DiffNode newRoot = DiffNode.createRoot(DiffLinesLabel.ofCodeBlock(DIFFGRAPH_LABEL));
nodes.stream()
.filter(DiffNode::isRoot)
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/ProjectionSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/ProjectionSource.java
index 7f277b69c..5f6225af1 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/ProjectionSource.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/ProjectionSource.java
@@ -1,7 +1,23 @@
package org.variantsync.diffdetective.variation.diff;
+import java.util.List;
+
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.Label;
-import org.variantsync.diffdetective.variation.tree.source.VariationTreeSource;
-public record ProjectionSource(VariationDiff origin, Time time) implements VariationTreeSource {
+public record ProjectionSource(VariationDiff origin, Time time) implements Source {
+ @Override
+ public String getSourceExplanation() {
+ return "Projection";
+ }
+
+ @Override
+ public List getSources() {
+ return List.of(origin);
+ }
+
+ @Override
+ public List getSourceArguments() {
+ return List.of(time);
+ }
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareDiffer.java b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareDiffer.java
new file mode 100644
index 000000000..11e5a60fa
--- /dev/null
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareDiffer.java
@@ -0,0 +1,56 @@
+package org.variantsync.diffdetective.variation.diff;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.file.Path;
+
+import org.variantsync.diffdetective.diff.result.DiffParseException;
+import org.variantsync.diffdetective.util.FileSource;
+import org.variantsync.diffdetective.util.Source;
+import org.variantsync.diffdetective.variation.Label;
+import org.variantsync.diffdetective.variation.tree.VariationTree; // For Javadoc
+
+/**
+ * A generic interface for creating variation diffs from two states represented as text.
+ * This interface represents a differ that can walk any of the paths present in our visual abstract
+ * (see below) and thus the states before and after the edit are represented as text. For each of
+ * the two paths, parsing a text diff and diffing variation trees, there is a specialized version of
+ * this interface, {@link VariabilityAwareTextDiffer} and {@link VariabilityAwareTreeDiffer}
+ * respectively. In particular, {@link VariabilityAwareTreeDiffer} represents the input states as
+ * {@link VariationTree}s.
+ *
+ *
+ *
+ * @see VariabilityAwareDiffers
+ */
+@FunctionalInterface
+public interface VariabilityAwareDiffer {
+ /**
+ * Create a variation diff by diffing the content of two {@link Reader}s.
+ * For {@link VariationDiff#getSource() traceability}, both {@link Reader}s are paired with a {@link Source}.
+ */
+ VariationDiff diff(Reader before, Reader after, Source beforeSource, Source afterSource) throws IOException, DiffParseException;
+
+ /**
+ * Create a variation diff by diffing the content of two {@link String}s.
+ * For {@link VariationDiff#getSource() traceability}, both {@link String}s are paired with a {@link Source}.
+ */
+ default VariationDiff diff(String before, String after, Source beforeSource, Source afterSource) throws IOException, DiffParseException {
+ return diff(new StringReader(before), new StringReader(after), beforeSource, afterSource);
+ }
+
+ /**
+ * Create a variation diff by diffing the content of two {@link Path}s.
+ * @see diff(Reader, Reader, Source, Source)
+ */
+ default VariationDiff diff(Path before, Path after) throws IOException, DiffParseException {
+ try (
+ Reader beforeStream = new FileReader(before.toFile());
+ Reader afterStream = new FileReader(after.toFile())
+ ) {
+ return diff(beforeStream, afterStream, new FileSource(before), new FileSource(after));
+ }
+ }
+}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareDiffers.java b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareDiffers.java
new file mode 100644
index 000000000..ce790d696
--- /dev/null
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareDiffers.java
@@ -0,0 +1,135 @@
+package org.variantsync.diffdetective.variation.diff;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.commons.io.IOUtils;
+import org.eclipse.jgit.diff.DiffAlgorithm;
+import org.variantsync.diffdetective.diff.result.DiffParseException;
+import org.variantsync.diffdetective.util.CompositeSource;
+import org.variantsync.diffdetective.util.Source;
+import org.variantsync.diffdetective.variation.DiffLinesLabel;
+import org.variantsync.diffdetective.variation.diff.construction.GumTreeDiff;
+import org.variantsync.diffdetective.variation.diff.construction.JGitDiff;
+import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
+import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParser; // For Javadoc
+import org.variantsync.diffdetective.variation.tree.VariationTree;
+
+import com.github.gumtreediff.matchers.Matcher;
+import com.github.gumtreediff.matchers.Matchers;
+
+/**
+ * A collection of differs that create variation diffs.
+ * @see VariabilityAwareDiffer
+ * @see VariationDiff
+ */
+public class VariabilityAwareDiffers {
+ /**
+ * Returns a differ that {@link VariationDiffParser#createVariationDiff parses} a
+ * {@link VariationDiff} after {@link JGitDiff#textDiff diffing} with
+ * {@link DiffAlgorithm.SupportedAlgorithm one of JGit's differ}.
+ *
+ * @param lineDiffAlgorithm the Git diffing algorithm to use
+ * @param parseOptions options for parsing the {@link VariationDiff}
+ * @return the diffed {@link VariationDiff}
+ */
+ public static VariabilityAwareTextDiffer JGit(DiffAlgorithm.SupportedAlgorithm lineDiffAlgorithm, VariationDiffParseOptions parseOptions) {
+ return new VariabilityAwareTextDiffer() {
+ @Override
+ public String diffLines(Reader before, Reader after) throws IOException {
+ return JGitDiff.textDiff(IOUtils.toString(before), IOUtils.toString(after), lineDiffAlgorithm);
+ }
+
+ @Override
+ public String diffLines(String before, String after) throws IOException {
+ return JGitDiff.textDiff(before, after, lineDiffAlgorithm);
+ }
+
+ @Override
+ public VariationDiffParseOptions getParseOptions() {
+ return parseOptions;
+ }
+ };
+ }
+
+ /**
+ * Calls {@link JGit(DiffAlgorithm.SupportedAlgorithm, VariationDiffParseOptions)} with
+ * {@link DiffAlgorithm.SupportedAlgorithm#MYERS} and {@link VariationDiffParseOptions#Default}.
+ */
+ public static VariabilityAwareTextDiffer JGitMyers() {
+ return JGit(DiffAlgorithm.SupportedAlgorithm.MYERS, VariationDiffParseOptions.Default);
+ }
+
+ /**
+ * Returns a differ that {@link VariationTree#fromFile parses} two variation trees and then
+ * {@link GumTreeDiff#diffUsingMatching diffs} these variation trees.
+ *
+ * @param parseOptions options for parsing the {@link VariationTree}s
+ * @param matcher the matcher used for diffing the {@link VariationTree}s
+ * @return the diffed {@link VariationDiff}
+ */
+ public static VariabilityAwareTreeDiffer GumTree(VariationDiffParseOptions parseOptions, Matcher matcher) {
+ return new VariabilityAwareTreeDiffer() {
+ @Override
+ public VariationDiff diffTrees(VariationTree before, VariationTree after) {
+ return GumTreeDiff.diffUsingMatching(before, after, matcher);
+ }
+
+ @Override
+ public VariationDiffParseOptions getParseOptions() {
+ return parseOptions;
+ }
+ };
+ }
+
+ /**
+ * Calls {@link GumTree(VariationDiffParseOptions, Matcher)} with {@link
+ * VariationDiffParseOptions#Default} and {@link Matchers#getInstance {@code
+ * Matchers.getInstance().getMatcher()}}.
+ */
+ public static VariabilityAwareTreeDiffer GumTree() {
+ return GumTree(VariationDiffParseOptions.Default, Matchers.getInstance().getMatcher());
+ }
+
+ /**
+ * Returns a differ that first parses a {@link VariationDiff} like {@link JGit} and then
+ * {@link GumTreeDiff#improveMatching improves} the represented matching.
+ *
+ * @param lineDiffAlgorithm the Git diffing algorithm to use
+ * @param parseOptions options for parsing the {@link VariationDiff}
+ * @param matcher the matcher used for diffing the variation trees
+ * @return the diffed {@link VariationDiff}
+ */
+ public static VariabilityAwareTextDiffer JGitGumTreeHybrid(DiffAlgorithm.SupportedAlgorithm lineDiffAlgorithm, VariationDiffParseOptions parseOptions, Matcher matcher) {
+ return new VariabilityAwareTextDiffer() {
+ @Override
+ public String diffLines(Reader before, Reader after) throws IOException {
+ return JGitDiff.textDiff(IOUtils.toString(before), IOUtils.toString(after), lineDiffAlgorithm);
+ }
+
+ @Override
+ public VariationDiffParseOptions getParseOptions() {
+ return parseOptions;
+ }
+
+ @Override
+ public VariationDiff diff(Reader before, Reader after, Source beforeSource, Source afterSource) throws IOException, DiffParseException {
+ VariationDiff vd = VariabilityAwareTextDiffer.super.diff(before, after, beforeSource, afterSource);
+ return new VariationDiff<>(
+ GumTreeDiff.improveMatching(vd.getRoot(), matcher),
+ new CompositeSource("GumTreeDiff.improveMatching", vd.getSource())
+ );
+ };
+ };
+ }
+
+ /**
+ * Calls {@link JGitGumTreeHybrid(DiffAlgorithm.SupportedAlgorithm, VariationDiffParseOptions,
+ * Matcher)} with {@link DiffAlgorithm.SupportedAlgorithm#MYERS}, {@link
+ * VariationDiffParseOptions#Default}, and {@link Matchers#getInstance {@code
+ * Matchers.getInstance().getMatcher()}}.
+ */
+ public static VariabilityAwareTextDiffer JGitMyersGumTreeHybrid() {
+ return JGitGumTreeHybrid(DiffAlgorithm.SupportedAlgorithm.MYERS, VariationDiffParseOptions.Default, Matchers.getInstance().getMatcher());
+ }
+}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareTextDiffer.java b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareTextDiffer.java
new file mode 100644
index 000000000..0fb6042c9
--- /dev/null
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareTextDiffer.java
@@ -0,0 +1,62 @@
+package org.variantsync.diffdetective.variation.diff;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.variantsync.diffdetective.diff.result.DiffParseException;
+import org.variantsync.diffdetective.util.CompositeSource;
+import org.variantsync.diffdetective.util.Source;
+import org.variantsync.diffdetective.variation.DiffLinesLabel;
+import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
+import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParser;
+
+/**
+ * A {@link VariabilityAwareDiffer} that first creates a line diff and
+ * {@link VariationDiffParser#createVariationDiff parses} this line diff into a
+ * {@link VariationDiff}.
+ */
+@FunctionalInterface
+public interface VariabilityAwareTextDiffer extends VariabilityAwareDiffer {
+ /**
+ * Returns a line diff of {@code before} and {@code after} in the unified diff format.
+ */
+ String diffLines(Reader before, Reader after) throws IOException;
+
+ /**
+ * Returns a line diff of {@code before} and {@code after} in the unified diff format.
+ */
+ default String diffLines(String before, String after) throws IOException {
+ return diffLines(new StringReader(before), new StringReader(after));
+ }
+
+ /**
+ * Returns the options used for {@link parseDiff parsing} the {@link VariationDiff} in {@link parseDiff}.
+ */
+ default VariationDiffParseOptions getParseOptions() {
+ return VariationDiffParseOptions.Default;
+ }
+
+ /**
+ * Parses the {@link diffLines line diff} into a {@link VariationDiff} using the options
+ * from {@link getParseOptions}.
+ */
+ default VariationDiff parseDiff(String diff, Source diffSource) throws DiffParseException {
+ return VariationDiffParser.createVariationDiff(
+ diff,
+ new CompositeSource("VariationDiffParser.createVariationDiff", diffSource),
+ getParseOptions()
+ );
+ }
+
+ /**
+ * Composes {@link diffLines} and {@link parseDiff} to create a {@link VariationDiff}.
+ */
+ @Override
+ default VariationDiff diff(Reader before, Reader after, Source beforeSource, Source afterSource) throws IOException, DiffParseException {
+ return parseDiff(
+ diffLines(before, after),
+ new CompositeSource("line diff", beforeSource, afterSource)
+ );
+ }
+}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareTreeDiffer.java b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareTreeDiffer.java
new file mode 100644
index 000000000..ec7aa0a6c
--- /dev/null
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/VariabilityAwareTreeDiffer.java
@@ -0,0 +1,50 @@
+package org.variantsync.diffdetective.variation.diff;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+import org.variantsync.diffdetective.diff.result.DiffParseException;
+import org.variantsync.diffdetective.util.Source;
+import org.variantsync.diffdetective.variation.DiffLinesLabel;
+import org.variantsync.diffdetective.variation.Label;
+import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
+import org.variantsync.diffdetective.variation.tree.VariationTree;
+
+/**
+ * A {@link VariabilityAwareDiffer} that first {@link VariationTree#fromFile parses} {@link VariationTree}s and
+ * diffs these trees.
+ */
+@FunctionalInterface
+public interface VariabilityAwareTreeDiffer extends VariabilityAwareDiffer {
+ /**
+ * Creates a {@link VariationDiff} by diffing two variation trees.
+ */
+ VariationDiff diffTrees(VariationTree before, VariationTree after);
+
+ /**
+ * Returns the options used for parsing the {@link VariationTree}s in {@link parseTree}.
+ */
+ default VariationDiffParseOptions getParseOptions() {
+ return VariationDiffParseOptions.Default;
+ }
+
+ /**
+ * {@link VariationTree#fromFile Parses} {@code input} into a {@link VariationTree} using
+ * the options from {@code getParseOptions}.
+ */
+ default VariationTree parseTree(Reader input, Source source) throws IOException, DiffParseException {
+ return VariationTree.fromFile(new BufferedReader(input), source, getParseOptions());
+ }
+
+ /**
+ * Composes {@link parseTree} and {@link diffTrees} to create a {@link VariationDiff}.
+ */
+ @Override
+ default VariationDiff diff(Reader before, Reader after, Source beforeSource, Source afterSource) throws IOException, DiffParseException {
+ return diffTrees(
+ parseTree(before, beforeSource),
+ parseTree(after, afterSource)
+ );
+ }
+}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/VariationDiff.java b/src/main/java/org/variantsync/diffdetective/variation/diff/VariationDiff.java
index 0d6c365bb..fea28736f 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/VariationDiff.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/VariationDiff.java
@@ -12,19 +12,18 @@
import org.variantsync.diffdetective.diff.result.DiffError;
import org.variantsync.diffdetective.diff.result.DiffParseException;
import org.variantsync.diffdetective.util.Assert;
+import org.variantsync.diffdetective.util.FileSource;
+import org.variantsync.diffdetective.util.fide.FixTrueFalse;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.diff.construction.GumTreeDiff;
import org.variantsync.diffdetective.variation.diff.construction.JGitDiff;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParser;
-import org.variantsync.diffdetective.variation.diff.source.PatchFile;
-import org.variantsync.diffdetective.variation.diff.source.PatchString;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.diffdetective.variation.diff.traverse.VariationDiffTraversal;
import org.variantsync.diffdetective.variation.diff.traverse.VariationDiffVisitor;
import org.variantsync.diffdetective.variation.tree.VariationTree;
-import org.variantsync.diffdetective.util.fide.FixTrueFalse;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.functjonal.Cast;
import org.variantsync.functjonal.Result;
@@ -50,23 +49,23 @@
/**
* Implementation of variation tree diffs from our ESEC/FSE'22 paper.
* An instance of this class represents a variation tree diff. It stores the root of the graph as a {@link DiffNode}.
- * It optionally holds a {@link VariationDiffSource} that describes how the variation tree diff was obtained.
+ * It optionally holds a {@link Source} that describes how the variation tree diff was obtained.
* The graph structure is implemented by the {@link DiffNode} class.
*
* @param The type of label stored in this tree.
*
* @author Paul Bittner, Sören Viegener
*/
-public class VariationDiff {
+public class VariationDiff implements Source {
private final DiffNode root;
- private VariationDiffSource source;
+ private Source source;
/**
* Creates a VariationDiff that only consists of the single given root node.
* @param root The root of this tree.
*/
public VariationDiff(DiffNode root) {
- this(root, VariationDiffSource.Unknown);
+ this(root, Source.Unknown);
}
/**
@@ -75,7 +74,7 @@ public VariationDiff(DiffNode root) {
* @param root The root of this tree.
* @param source The data from which the VariationDiff was created.
*/
- public VariationDiff(DiffNode root, VariationDiffSource source) {
+ public VariationDiff(DiffNode root, Source source) {
this.root = root;
this.source = source;
}
@@ -91,9 +90,7 @@ public VariationDiff(DiffNode root, VariationDiffSource source) {
*/
public static VariationDiff fromFile(final Path p, VariationDiffParseOptions parseOptions) throws IOException, DiffParseException {
try (BufferedReader file = Files.newBufferedReader(p)) {
- final VariationDiff tree = VariationDiffParser.createVariationDiff(file, parseOptions);
- tree.setSource(new PatchFile(p));
- return tree;
+ return VariationDiffParser.createVariationDiff(file, new FileSource(p), parseOptions);
}
}
@@ -103,13 +100,13 @@ public static VariationDiff fromFile(final Path p, VariationDiff
* So just lines preceded by "+", "-", or " " are expected.
* @param diff The diff as text. Lines should be separated by a newline character. Each line should be preceded by either "+", "-", or " ".
* @param parseOptions {@link VariationDiffParseOptions} for the parsing process.
+ * @param source the {@link Source} of {@code diff}
* @return A result either containing the parsed VariationDiff or an error message in case of failure.
* @throws DiffParseException if {@code diff} couldn't be parsed
*/
- public static VariationDiff fromDiff(final String diff, final VariationDiffParseOptions parseOptions) throws DiffParseException {
- final VariationDiff d;
+ public static VariationDiff fromDiff(final String diff, final Source source, final VariationDiffParseOptions parseOptions) throws DiffParseException {
try {
- d = VariationDiffParser.createVariationDiff(diff, parseOptions);
+ return VariationDiffParser.createVariationDiff(diff, source, parseOptions);
} catch (DiffParseException e) {
Logger.error("""
Could not parse diff:
@@ -119,8 +116,6 @@ public static VariationDiff fromDiff(final String diff, final Va
diff);
throw e;
}
- d.setSource(new PatchString(diff));
- return d;
}
/**
@@ -164,7 +159,7 @@ public static Result, List> fromPatch(f
/**
* Create a VariationDiff from two given text files.
- * @see #fromLines(String, String, DiffAlgorithm.SupportedAlgorithm, VariationDiffParseOptions)
+ * @see #fromLines(String, String, Source, Source, DiffAlgorithm.SupportedAlgorithm, VariationDiffParseOptions)
*/
public static VariationDiff fromFiles(
final Path beforeFile,
@@ -176,22 +171,24 @@ public static VariationDiff fromFiles(
try (BufferedReader b = Files.newBufferedReader(beforeFile);
BufferedReader a = Files.newBufferedReader(afterFile)
) {
- return fromLines(IOUtils.toString(b), IOUtils.toString(a), algorithm, options);
+ return fromLines(IOUtils.toString(b), IOUtils.toString(a), new FileSource(beforeFile), new FileSource(afterFile), algorithm, options);
}
}
/**
* Creates a variation diff from to line-based text inputs.
* This method just forwards to:
- * @see JGitDiff#diff(String, String, DiffAlgorithm.SupportedAlgorithm, VariationDiffParseOptions)
+ * @see JGitDiff#diff(String, String, Source, Source, DiffAlgorithm.SupportedAlgorithm, VariationDiffParseOptions)
*/
public static VariationDiff fromLines(
String before,
String after,
+ Source beforeSource,
+ Source afterSource,
DiffAlgorithm.SupportedAlgorithm algorithm,
VariationDiffParseOptions options) throws IOException, DiffParseException
{
- return JGitDiff.diff(before, after, algorithm, options);
+ return JGitDiff.diff(before, after, beforeSource, afterSource, algorithm, options);
}
/**
@@ -396,20 +393,30 @@ public LinkedHashSet computeAllFeatureNames() {
/**
* Sets the source of this VariationDiff.
- * @see VariationDiffSource
+ * @see Source
*/
- public void setSource(final VariationDiffSource source) {
+ public void setSource(final Source source) {
this.source = source;
}
/**
* Returns the source of this VariationDiff (i.e., the data this VariationDiff was created from).
- * @see VariationDiffSource
+ * @see Source
*/
- public VariationDiffSource getSource() {
+ public Source getSource() {
return source;
}
+ @Override
+ public List getSources() {
+ return List.of(source);
+ }
+
+ @Override
+ public String getSourceExplanation() {
+ return "VariationDiff";
+ }
+
/**
* Returns the number of nodes in this VariationDiff.
*/
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiff.java b/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiff.java
index f7508ee41..f565ce20a 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiff.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiff.java
@@ -2,13 +2,13 @@
import org.variantsync.diffdetective.diff.text.DiffLineNumberRange;
import org.variantsync.diffdetective.util.Assert;
+import org.variantsync.diffdetective.util.CompositeSource;
import org.variantsync.diffdetective.util.StringUtils;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.diff.DiffNode;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.DiffType;
import org.variantsync.diffdetective.variation.diff.Time;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.diffdetective.variation.tree.VariationTree;
import org.variantsync.diffdetective.variation.tree.VariationTreeNode;
import org.variantsync.functjonal.Cast;
@@ -309,7 +309,7 @@ public EdgeToConstruct(
}
return new BadVDiff<>(
- new VariationTree<>(root, new BadVDiffFromVariationDiffSource(d.getSource())),
+ new VariationTree<>(root, new CompositeSource("BadVDiff.fromGood", d.getSource())),
matching,
coloring,
lines
@@ -403,12 +403,7 @@ public EdgeToConstruct(
nodeTranslation.get(e.parent()).addChild(e.child(), e.time());
}
- VariationDiffSource source = VariationDiffSource.Unknown;
- if (diff.source() instanceof BadVDiffFromVariationDiffSource s) {
- source = s.initialVariationDiff();
- }
-
- return new VariationDiff<>(root, source);
+ return new VariationDiff<>(root, new CompositeSource("BadVDiff.toGood", diff.getSource()));
}
public BadVDiff deepCopy() {
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiffFromVariationDiffSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiffFromVariationDiffSource.java
deleted file mode 100644
index ceb69d515..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/bad/BadVDiffFromVariationDiffSource.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.variantsync.diffdetective.variation.diff.bad;
-
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
-import org.variantsync.diffdetective.variation.tree.source.VariationTreeSource;
-
-public record BadVDiffFromVariationDiffSource(VariationDiffSource initialVariationDiff) implements VariationTreeSource {
- @Override
- public String toString() {
- return initialVariationDiff.toString();
- }
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/construction/GumTreeDiff.java b/src/main/java/org/variantsync/diffdetective/variation/diff/construction/GumTreeDiff.java
index bb0d37309..83273dce3 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/construction/GumTreeDiff.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/construction/GumTreeDiff.java
@@ -9,11 +9,11 @@
import org.variantsync.diffdetective.gumtree.VariationDiffAdapter;
import org.variantsync.diffdetective.gumtree.VariationTreeAdapter;
import org.variantsync.diffdetective.util.Assert;
+import org.variantsync.diffdetective.util.CompositeSource;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.diff.DiffNode;
import org.variantsync.diffdetective.variation.diff.Time;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
-import org.variantsync.diffdetective.variation.diff.source.VariationTreeDiffSource;
import org.variantsync.diffdetective.variation.diff.traverse.VariationDiffTraversal;
import org.variantsync.diffdetective.variation.tree.VariationNode;
import org.variantsync.diffdetective.variation.tree.VariationTree;
@@ -48,7 +48,7 @@ public static VariationDiff diffUsingMatching(VariationTree
matcher
);
- return new VariationDiff<>(root, new VariationTreeDiffSource(before.source(), after.source()));
+ return new VariationDiff<>(root, new CompositeSource("diffUsingMatching", before.source(), after.source()));
}
/**
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/construction/JGitDiff.java b/src/main/java/org/variantsync/diffdetective/variation/diff/construction/JGitDiff.java
index 6d2920585..abd988811 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/construction/JGitDiff.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/construction/JGitDiff.java
@@ -4,6 +4,8 @@
import org.eclipse.jgit.diff.*;
import org.variantsync.diffdetective.diff.git.GitDiffer;
import org.variantsync.diffdetective.diff.result.DiffParseException;
+import org.variantsync.diffdetective.util.CompositeSource;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.diff.Time;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
@@ -122,9 +124,11 @@ Using our own formatter without diff headers (paired with a maximum context (?))
* Uses JGit to diff the two files using the specified {@code options}, and afterwards, creates the variation diff.
* Creates a variation diff from to line-based text inputs.
* First creates a line-based diff with {@link #textDiff(String, String, DiffAlgorithm.SupportedAlgorithm)}
- * and then parses that diff with {@link VariationDiff#fromDiff(String, VariationDiffParseOptions)}.
+ * and then parses that diff with {@link VariationDiff#fromDiff(String, Source, VariationDiffParseOptions)}.
* @param linesBefore State of annotated lines before the change.
* @param linesAfter State of annotated lines after the change.
+ * @param sourceBefore the {@link Source} of {@code linesBefore}
+ * @param sourceAfter the {@link Source} of {@code linesAfter}
* @param algorithm Specification of which algorithm to use for diffing with JGit.
* @param options various options for parsing
* @return A variation diff comprising the changes.
@@ -134,9 +138,15 @@ Using our own formatter without diff headers (paired with a maximum context (?))
public static VariationDiff diff(
String linesBefore,
String linesAfter,
+ Source sourceBefore,
+ Source sourceAfter,
DiffAlgorithm.SupportedAlgorithm algorithm,
VariationDiffParseOptions options
) throws IOException, DiffParseException {
- return VariationDiff.fromDiff(textDiff(linesBefore, linesAfter, algorithm), options);
+ return VariationDiff.fromDiff(
+ textDiff(linesBefore, linesAfter, algorithm),
+ new CompositeSource("JGitDiff.textDiff", sourceBefore, sourceAfter),
+ options
+ );
}
}
diff --git a/docs/variability-aware-differencing.png b/src/main/java/org/variantsync/diffdetective/variation/diff/doc-files/variability-aware-differencing.png
similarity index 100%
rename from docs/variability-aware-differencing.png
rename to src/main/java/org/variantsync/diffdetective/variation/diff/doc-files/variability-aware-differencing.png
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/parse/VariationDiffParser.java b/src/main/java/org/variantsync/diffdetective/variation/diff/parse/VariationDiffParser.java
index 9f11bc653..f8b1f2cc2 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/parse/VariationDiffParser.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/parse/VariationDiffParser.java
@@ -17,6 +17,7 @@
import org.variantsync.diffdetective.feature.Annotation;
import org.variantsync.diffdetective.feature.AnnotationType;
import org.variantsync.diffdetective.util.Assert;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.NodeType;
import org.variantsync.diffdetective.variation.diff.DiffNode;
@@ -117,17 +118,18 @@ public record DiffLine(DiffType diffType, String content) {
/**
- * The same as {@link VariationDiffParser#createVariationDiff(BufferedReader, VariationDiffParseOptions)}
+ * The same as {@link VariationDiffParser#createVariationDiff(BufferedReader, Source, VariationDiffParseOptions)}
* but with the diff given as a single string with line breaks instead of a {@link BufferedReader}.
*
* @throws DiffParseException if {@code fullDiff} couldn't be parsed
*/
public static VariationDiff createVariationDiff(
final String fullDiff,
+ final Source source,
final VariationDiffParseOptions parseOptions
) throws DiffParseException {
try {
- return createVariationDiff(new BufferedReader(new StringReader(fullDiff)), parseOptions);
+ return createVariationDiff(new BufferedReader(new StringReader(fullDiff)), source, parseOptions);
} catch (IOException e) {
throw new AssertionError("No actual IO should be performed because only a StringReader is used");
}
@@ -140,6 +142,7 @@ public static VariationDiff createVariationDiff(
* This parsing algorithm is described in detail in Sören Viegener's bachelor's thesis.
*
* @param fullDiff The full diff of a patch obtained from a buffered reader.
+ * @param source the {@link Source} of {@code fullDiff}
* @param options {@link VariationDiffParseOptions} for the parsing process.
* @return A parsed {@link VariationDiff} upon success or an error indicating why parsing failed.
* @throws IOException when reading from {@code fullDiff} fails.
@@ -147,11 +150,12 @@ public static VariationDiff createVariationDiff(
*/
public static VariationDiff createVariationDiff(
BufferedReader fullDiff,
+ Source source,
final VariationDiffParseOptions options
) throws IOException, DiffParseException {
return new VariationDiffParser(
options
- ).parse(() -> {
+ ).parse(source, () -> {
String line = fullDiff.readLine();
if (line == null) {
return null;
@@ -162,10 +166,11 @@ public static VariationDiff createVariationDiff(
/**
* Parses a variation tree from a source file.
- * This method is similar to {@link #createVariationDiff(BufferedReader, VariationDiffParseOptions)}
+ * This method is similar to {@link #createVariationDiff(BufferedReader, Source, VariationDiffParseOptions)}
* but acts as if all lines where unmodified.
*
* @param file The source code file (not a diff) to be parsed.
+ * @param source the {@link Source} of {@code file}
* @param options {@link VariationDiffParseOptions} for the parsing process.
* @return A parsed {@link VariationDiff}.
* @throws IOException iff {@code file} throws an {@code IOException}
@@ -173,11 +178,12 @@ public static VariationDiff createVariationDiff(
*/
public static VariationDiff createVariationTree(
BufferedReader file,
+ Source source,
VariationDiffParseOptions options
) throws IOException, DiffParseException {
return new VariationDiffParser(
options
- ).parse(() -> {
+ ).parse(source, () -> {
String line = file.readLine();
if (line == null) {
return null;
@@ -198,7 +204,7 @@ public static VariationDiff createVariationTree(
/**
* Initializes the parse state.
*
- * @see #createVariationDiff(BufferedReader, VariationDiffParseOptions)
+ * @see #createVariationDiff(BufferedReader, Source, VariationDiffParseOptions)
*/
private VariationDiffParser(
VariationDiffParseOptions options
@@ -209,6 +215,7 @@ private VariationDiffParser(
/**
* Parses the line diff {@code fullDiff}.
*
+ * @param source the {@link Source} of {@code lines}
* @param lines should supply successive lines of the diff to be parsed, or {@code null} if
* there are no more lines to be parsed.
* @return the parsed {@code VariationDiff}
@@ -217,6 +224,7 @@ private VariationDiffParser(
* is detected
*/
private VariationDiff parse(
+ Source source,
FailableSupplier lines
) throws IOException, DiffParseException {
DiffNode root = DiffNode.createRoot(new DiffLinesLabel());
@@ -297,7 +305,7 @@ private VariationDiff parse(
);
}
- return new VariationDiff<>(root);
+ return new VariationDiff<>(root, source);
}
/**
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/render/VariationDiffRenderer.java b/src/main/java/org/variantsync/diffdetective/variation/diff/render/VariationDiffRenderer.java
index 91b03e0b9..a6dacc622 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/render/VariationDiffRenderer.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/render/VariationDiffRenderer.java
@@ -8,6 +8,7 @@
import org.variantsync.diffdetective.shell.ShellExecutor;
import org.variantsync.diffdetective.util.Assert;
import org.variantsync.diffdetective.util.IO;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.util.StringUtils;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.Label;
@@ -17,7 +18,6 @@
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphConstants;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphExport;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphExportOptions;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import java.io.IOException;
import java.nio.file.Files;
@@ -193,7 +193,7 @@ public boolean renderWithTreeFormat(final VariationDiff exte
* The function is invoked on the given treeAndFileName as first argument and the given VariationDiff's source as second argument.
* @return True iff rendering was successful. False iff an error occurred.
*/
- private boolean render(final VariationDiff extends L> tree, final String treeAndFileName, final Path directory, RenderOptions super L> options, LineGraphExportOptions super L> exportOptions, BiFunction treeHeader) {
+ private boolean render(final VariationDiff extends L> tree, final String treeAndFileName, final Path directory, RenderOptions super L> options, LineGraphExportOptions super L> exportOptions, BiFunction treeHeader) {
final Path tempFile = directory.resolve(treeAndFileName + ".lg");
try (var destination = IO.newBufferedOutputStream(tempFile)) {
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphExport.java b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphExport.java
index d1483b202..fe9b8319a 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphExport.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphExport.java
@@ -10,12 +10,12 @@
import org.variantsync.diffdetective.diff.git.CommitDiff;
import org.variantsync.diffdetective.diff.git.PatchDiff;
import org.variantsync.diffdetective.metadata.Metadata;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.util.StringUtils;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.diff.Time;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.functjonal.category.InplaceSemigroup;
/**
@@ -154,12 +154,12 @@ public static Statistic toLineGraphFormat(final PatchDiff patch, final LineGraph
/**
* Produces the final linegraph file content.
- * Creates a linegraph header from the given VariationDiffSource using the {@link LineGraphExportOptions#treeFormat()} in the given options.
+ * Creates a linegraph header from the given Source using the {@link LineGraphExportOptions#treeFormat()} in the given options.
* Then appends the already created file content for nodes and edges.
- * @param source The {@link VariationDiffSource} that describes where the VariationDiff whose content is written to the file originated from.
+ * @param source The {@link Source} that describes where the VariationDiff whose content is written to the file originated from.
* @param options {@link LineGraphExportOptions} used to determine the treeFormat for the header.
*/
- private static String lineGraphHeader(final VariationDiffSource source, final LineGraphExportOptions> options) {
+ private static String lineGraphHeader(final Source source, final LineGraphExportOptions> options) {
return options.treeFormat().toLineGraphLine(source) + StringUtils.LINEBREAK;
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphImport.java b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphImport.java
index 3d9750b98..dcb323eb3 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphImport.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/LineGraphImport.java
@@ -2,10 +2,10 @@
import org.variantsync.diffdetective.util.Assert;
import org.variantsync.diffdetective.util.FileUtils;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.diff.DiffGraph;
import org.variantsync.diffdetective.variation.diff.DiffNode;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.diffdetective.variation.diff.source.LineGraphFileSource;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.NodeType;
@@ -128,9 +128,9 @@ public static List> fromLineGraph(final BufferedRe
* @return {@link VariationDiff} generated from the given, already parsed parameters.
*/
private static VariationDiff parseVariationDiff(final String lineGraph, final Path inFile, final List> diffNodeList, final LineGraphImportOptions options) {
- VariationDiffSource variationDiffSource = options.treeFormat().fromLineGraphLine(lineGraph);
+ Source variationDiffSource = options.treeFormat().fromLineGraphLine(lineGraph);
- if (variationDiffSource == null || VariationDiffSource.Unknown.equals(variationDiffSource)) {
+ if (variationDiffSource == null || Source.Unknown.equals(variationDiffSource)) {
variationDiffSource = new LineGraphFileSource(
lineGraph,
inFile
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/nodeformat/DiffNodeLabelFormat.java b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/nodeformat/DiffNodeLabelFormat.java
index 378497db9..49a26b641 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/nodeformat/DiffNodeLabelFormat.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/nodeformat/DiffNodeLabelFormat.java
@@ -7,7 +7,6 @@
import org.variantsync.diffdetective.variation.diff.DiffNode;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphConstants;
import org.variantsync.diffdetective.variation.diff.serialize.LinegraphFormat;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.functjonal.Pair;
/**
@@ -49,7 +48,7 @@ default List toMultilineLabel(DiffNode extends L> node) {
}
/**
- * Converts a line describing a graph (starting with "t # ") in line graph format into a {@link VariationDiffSource}.
+ * Converts a line describing a graph (starting with "t # ") in line graph format into a {@link DiffNode}.
*
* @param lineGraphLine A line from a line graph file starting with "t #"
* @return A pair with the first element being the id of the node specified in the given lineGrapLine.
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/CommitDiffVariationDiffLabelFormat.java b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/CommitDiffVariationDiffLabelFormat.java
index c4d19637e..dad73a7bc 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/CommitDiffVariationDiffLabelFormat.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/CommitDiffVariationDiffLabelFormat.java
@@ -3,10 +3,10 @@
import org.apache.commons.io.FilenameUtils;
import org.variantsync.diffdetective.diff.git.CommitDiff;
import org.variantsync.diffdetective.diff.git.PatchDiff;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.diff.Time;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphConstants;
import org.variantsync.diffdetective.variation.diff.source.CommitDiffVariationDiffSource;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
@@ -19,7 +19,7 @@
*/
public class CommitDiffVariationDiffLabelFormat implements VariationDiffLabelFormat {
@Override
- public VariationDiffSource fromLabel(final String label) {
+ public Source fromLabel(final String label) {
String[] commit = label.split(LineGraphConstants.TREE_NAME_SEPARATOR_REGEX);
try {
Path filePath = Paths.get(commit[0]);
@@ -31,15 +31,19 @@ public VariationDiffSource fromLabel(final String label) {
}
@Override
- public String toLabel(final VariationDiffSource variationDiffSource) {
- if (variationDiffSource instanceof CommitDiffVariationDiffSource commitDiffVariationDiffSource) {
+ public String toLabel(final Source variationDiffSource) {
+ CommitDiffVariationDiffSource commitDiffVariationDiffSource = Source.findFirst(variationDiffSource, CommitDiffVariationDiffSource.class);
+ if (commitDiffVariationDiffSource != null) {
// write for instances of CommitDiffVariationDiffSources
return FilenameUtils.separatorsToUnix(commitDiffVariationDiffSource.getFileName().toString()) + LineGraphConstants.TREE_NAME_SEPARATOR + commitDiffVariationDiffSource.getCommitHash();
- } else if (variationDiffSource instanceof PatchDiff patchDiff) {
+ }
+
+ PatchDiff patchDiff = Source.findFirst(variationDiffSource, PatchDiff.class);
+ if (patchDiff != null) {
// write for instances of PatchDiffs
return FilenameUtils.separatorsToUnix(patchDiff.getFileName(Time.AFTER)) + LineGraphConstants.TREE_NAME_SEPARATOR + patchDiff.getCommitDiff().getCommitHash();
- } else {
- throw new UnsupportedOperationException("There is no implementation for this VariationDiffSource type: " + variationDiffSource);
}
+
+ throw new UnsupportedOperationException("There is no implementation for this Source type: " + variationDiffSource);
}
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/IndexedTreeFormat.java b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/IndexedTreeFormat.java
index 45a44a64d..67f7d351e 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/IndexedTreeFormat.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/IndexedTreeFormat.java
@@ -1,10 +1,10 @@
package org.variantsync.diffdetective.variation.diff.serialize.treeformat;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
+import org.variantsync.diffdetective.util.Source;
/**
* Exports tree by indexing them.
- * This format keeps an internal counter that is incremented on each call of {@link #toLabel(VariationDiffSource)}.
+ * This format keeps an internal counter that is incremented on each call of {@link #toLabel(Source)}.
* Thus, every produced label will have the successive index of the previously produced label.
*/
public class IndexedTreeFormat implements VariationDiffLabelFormat {
@@ -25,12 +25,12 @@ public void reset() {
}
@Override
- public VariationDiffSource fromLabel(String label) {
- return VariationDiffSource.Unknown;
+ public Source fromLabel(String label) {
+ return Source.Unknown;
}
@Override
- public String toLabel(VariationDiffSource variationDiffSource) {
+ public String toLabel(Source variationDiffSource) {
final String result = "" + nextId;
++nextId;
return result;
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/VariationDiffLabelFormat.java b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/VariationDiffLabelFormat.java
index f9c5d5ebb..ec3c6c56f 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/VariationDiffLabelFormat.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/serialize/treeformat/VariationDiffLabelFormat.java
@@ -1,47 +1,47 @@
package org.variantsync.diffdetective.variation.diff.serialize.treeformat;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphConstants;
import org.variantsync.diffdetective.variation.diff.serialize.LinegraphFormat;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
/**
- * Reads and writes {@link VariationDiffSource} from and to line graph.
+ * Reads and writes {@link Source} from and to line graph.
* @author Paul Bittner, Kevin Jedelhauser
*/
public interface VariationDiffLabelFormat extends LinegraphFormat {
/**
- * Converts a label of line graph into a {@link VariationDiffSource}.
+ * Converts a label of line graph into a {@link Source}.
*
- * @param label A string containing the label of the {@link VariationDiffSource}
- * @return The {@link VariationDiffSource} descibed by this label.
+ * @param label A string containing the label of the {@link Source}
+ * @return The {@link Source} described by this label.
*/
- VariationDiffSource fromLabel(final String label);
+ Source fromLabel(final String label);
/**
- * Converts a {@link VariationDiffSource} label of line graph.
+ * Converts a {@link Source} label of line graph.
*
- * @param variationDiffSource The {@link VariationDiffSource} to be converted
+ * @param variationDiffSource The {@link Source} to be converted
* @return The corresponding line graph line
*/
- String toLabel(final VariationDiffSource variationDiffSource);
+ String toLabel(final Source variationDiffSource);
/**
- * Converts a line describing a graph (starting with "t # ") in line graph format into a {@link VariationDiffSource}.
+ * Converts a line describing a graph (starting with "t # ") in line graph format into a {@link Source}.
*
* @param lineGraphLine A line from a line graph file starting with "t #"
- * @return The {@link VariationDiffSource} descibed by the label of this line.
+ * @return The {@link Source} descibed by the label of this line.
*/
- default VariationDiffSource fromLineGraphLine(final String lineGraphLine) {
+ default Source fromLineGraphLine(final String lineGraphLine) {
return fromLabel(lineGraphLine.substring((LineGraphConstants.LG_TREE_HEADER + " ").length()));
}
/**
* Prepends the {@link LineGraphConstants#LG_TREE_HEADER tree declaration} to a label and return an entire line graph line.
*
- * @param variationDiffSource The {@link VariationDiffSource} to be converted
- * @return The entire line graph line of a {@link VariationDiffSource}.
+ * @param variationDiffSource The {@link Source} to be converted
+ * @return The entire line graph line of a {@link Source}.
*/
- default String toLineGraphLine(final VariationDiffSource variationDiffSource) {
+ default String toLineGraphLine(final Source variationDiffSource) {
return LineGraphConstants.LG_TREE_HEADER + " " + toLabel(variationDiffSource);
}
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/CommitDiffVariationDiffSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/CommitDiffVariationDiffSource.java
index 7b12106b7..b41b07397 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/CommitDiffVariationDiffSource.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/source/CommitDiffVariationDiffSource.java
@@ -1,42 +1,29 @@
package org.variantsync.diffdetective.variation.diff.source;
import java.nio.file.Path;
+import java.util.List;
import org.variantsync.diffdetective.diff.git.CommitDiff; // For Javadoc
+import org.variantsync.diffdetective.util.Source;
/**
* Describes that a VariationDiff was created from a patch in a {@link CommitDiff}.
+ * @param getFileName Name of the modified file from whose changes the VariationDiff was parsed.
+ * @param getCommitHash Hash of the commit in which the edit occurred.
*/
-public class CommitDiffVariationDiffSource implements VariationDiffSource {
- private final Path fileName;
- private final String commitHash;
-
- /**
- * Create a source that refers to changes to the given file in the given commit.
- * @param fileName Name of the modified file from whose changes the VariationDiff was parsed.
- * @param commitHash Hash of the commit in which the edit occurred.
- */
- public CommitDiffVariationDiffSource(final Path fileName, final String commitHash) {
- this.fileName = fileName;
- this.commitHash = commitHash;
- }
-
- /**
- * Returns the name of the modified file from whose changes the VariationDiff was parsed.
- */
- public Path getFileName() {
- return fileName;
+public record CommitDiffVariationDiffSource(Path getFileName, String getCommitHash) implements Source {
+ @Override
+ public String toString() {
+ return getFileName() + "@" + getCommitHash();
}
- /**
- * Returns the hash of the commit in which the edit occurred.
- */
- public String getCommitHash() {
- return commitHash;
+ @Override
+ public String getSourceExplanation() {
+ return "CommitDiff";
}
@Override
- public String toString() {
- return fileName + "@" + commitHash;
+ public List getSourceArguments() {
+ return List.of(getFileName(), getCommitHash());
}
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/FromVariationTreeSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/FromVariationTreeSource.java
deleted file mode 100644
index 29ee2a23c..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/FromVariationTreeSource.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.variantsync.diffdetective.variation.diff.source;
-
-import org.variantsync.diffdetective.variation.tree.source.VariationTreeSource;
-
-public record FromVariationTreeSource(VariationTreeSource inner) implements VariationDiffSource {
- @Override
- public String toString() {
- return inner.toString();
- }
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/LineGraphFileSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/LineGraphFileSource.java
index 4b2d73e9d..7b25f2a39 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/LineGraphFileSource.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/source/LineGraphFileSource.java
@@ -1,6 +1,9 @@
package org.variantsync.diffdetective.variation.diff.source;
import java.nio.file.Path;
+import java.util.List;
+
+import org.variantsync.diffdetective.util.Source;
/**
* A source for VariationDiffs that were parsed from a linegraph file.
@@ -10,5 +13,14 @@
public record LineGraphFileSource(
String graphHeader,
Path file
-) implements VariationDiffSource {
+) implements Source {
+ @Override
+ public String getSourceExplanation() {
+ return "LineGraphFile";
+ }
+
+ @Override
+ public List getSourceArguments() {
+ return List.of(graphHeader, file);
+ }
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchFile.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchFile.java
deleted file mode 100644
index 060b909c6..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchFile.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.variantsync.diffdetective.variation.diff.source;
-
-import java.nio.file.Path;
-
-/**
- * A source for VariationDiff's that were created from patch files on disk.
- * @param path Path to the patch file that was parsed to a VariationDiff.
- */
-public record PatchFile(Path path) implements VariationDiffSource {
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchString.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchString.java
index 610029183..99feb00ca 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchString.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/source/PatchString.java
@@ -1,14 +1,20 @@
package org.variantsync.diffdetective.variation.diff.source;
import org.variantsync.diffdetective.diff.text.TextBasedDiff;
+import org.variantsync.diffdetective.util.Source;
/**
* Source for VariationDiffs that were created from a patch given as a String.
* @param getDiff The patch as a String.
*/
-public record PatchString(String getDiff) implements TextBasedDiff, VariationDiffSource {
+public record PatchString(String getDiff) implements TextBasedDiff, Source {
+ @Override
+ public String getSourceExplanation() {
+ return "Patch";
+ }
+
@Override
public String toString() {
- return "from line-based diff " + getDiff;
+ return getSourceExplanation() + getDiff;
}
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/VariationDiffSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/VariationDiffSource.java
deleted file mode 100644
index 01f5a944c..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/VariationDiffSource.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.variantsync.diffdetective.variation.diff.source;
-
-/**
- * Describes or identifies that data a VariationDiff was created or parsed from.
- * This is typically a patch.
- */
-public interface VariationDiffSource {
- /**
- * Constant to use when the source of a VariationDiff is unknown
- * or if it was created artificially.
- */
- VariationDiffSource Unknown = new VariationDiffSource() {
- @Override
- public int hashCode() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "Unknown VariationDiffSource";
- }
- };
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/source/VariationTreeDiffSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/source/VariationTreeDiffSource.java
deleted file mode 100644
index f5235f866..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/source/VariationTreeDiffSource.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.variantsync.diffdetective.variation.diff.source;
-
-import org.variantsync.diffdetective.variation.diff.VariationDiff; // For Javadoc
-import org.variantsync.diffdetective.variation.tree.VariationTree; // For Javadoc
-import org.variantsync.diffdetective.variation.tree.source.VariationTreeSource;
-
-/**
- * Describes that a {@link VariationDiff} was created from two {@link VariationTree}s.
- */
-public record VariationTreeDiffSource(
- VariationTreeSource before,
- VariationTreeSource after
-) implements VariationDiffSource {
- @Override
- public String toString() {
- return "from trees [" + before + "] and [" + after + "]";
- }
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/view/DiffView.java b/src/main/java/org/variantsync/diffdetective/variation/diff/view/DiffView.java
index ebf9e0238..65e097131 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/view/DiffView.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/view/DiffView.java
@@ -6,6 +6,7 @@
import org.variantsync.diffdetective.experiments.views.Main;
import org.variantsync.diffdetective.util.Assert;
import org.variantsync.diffdetective.util.CollectionUtils;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.diff.*;
@@ -72,12 +73,19 @@ public static BiPredicate> computeWhenNode
private static VariationDiff naive(final VariationDiff d, final Relevance rho, final String[] projectionViewText) throws IOException, DiffParseException {
final VariationDiff view;
try {
- view = JGitDiff.diff(projectionViewText[0], projectionViewText[1], DiffAlgorithm.SupportedAlgorithm.MYERS, Main.VARIATION_DIFF_PARSE_OPTIONS);
+ view = JGitDiff.diff(
+ projectionViewText[0],
+ projectionViewText[1],
+ Source.Unknown, // overridden below
+ Source.Unknown, // overridden below
+ DiffAlgorithm.SupportedAlgorithm.MYERS,
+ Main.VARIATION_DIFF_PARSE_OPTIONS
+ );
} catch (DiffParseException e) {
Logger.error("Could not parse diff obtained with query {} at {}", d.getSource(), rho);
throw e;
}
- view.setSource(new ViewSource<>(d, rho));
+ view.setSource(new ViewSource<>(d, rho, "naive"));
return view;
}
@@ -153,6 +161,7 @@ public static VariationDiff badgood(final VariationDiff
// unify
final VariationDiff goodDiff = badDiff.toGood();
+ goodDiff.setSource(new ViewSource<>(d, rho, "badgood"));
goodDiff.assertConsistency();
return goodDiff;
}
@@ -261,7 +270,7 @@ record Edge(DiffNode childCopy, DiffNode parentInD, Time
// Step 4: Build return value
Assert.assertNotNull(rootCopy[0]);
- return new VariationDiff<>(rootCopy[0], new ViewSource<>(d, rho));
+ return new VariationDiff<>(rootCopy[0], new ViewSource<>(d, rho, "optimized"));
}
/**
diff --git a/src/main/java/org/variantsync/diffdetective/variation/diff/view/ViewSource.java b/src/main/java/org/variantsync/diffdetective/variation/diff/view/ViewSource.java
index 6e1f0fabf..78b85b0b0 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/diff/view/ViewSource.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/diff/view/ViewSource.java
@@ -1,15 +1,31 @@
package org.variantsync.diffdetective.variation.diff.view;
+import java.util.List;
+
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.diffdetective.variation.tree.view.relevance.Relevance;
/**
- * A {@link VariationDiffSource} that remembers that a variation diff represents a view on
- * another variation diff.
+ * A {@link Source} that remembers that a variation diff represents a view on another variation
+ * diff.
* @param diff The original variation diff on which the variation diff with this source is a view on.
* @param relevance The relevance predicate that was used to create the view.
*/
-public record ViewSource(VariationDiff diff, Relevance relevance) implements VariationDiffSource {
+public record ViewSource(VariationDiff diff, Relevance relevance, String method) implements Source {
+ @Override
+ public String getSourceExplanation() {
+ return "view";
+ }
+
+ @Override
+ public List getSources() {
+ return List.of(diff);
+ }
+
+ @Override
+ public List getSourceArguments() {
+ return List.of(relevance);
+ }
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java
index 9920b3e6a..24a0eb3f1 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/VariationTree.java
@@ -3,17 +3,17 @@
import org.variantsync.diffdetective.datasets.PatchDiffParseOptions;
import org.variantsync.diffdetective.diff.result.DiffParseException;
import org.variantsync.diffdetective.util.Assert;
+import org.variantsync.diffdetective.util.CompositeSource;
+import org.variantsync.diffdetective.util.FileSource;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.Label;
import org.variantsync.diffdetective.variation.NodeType; // For Javadoc
import org.variantsync.diffdetective.variation.diff.DiffNode;
-import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.Projection;
+import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParser;
-import org.variantsync.diffdetective.variation.diff.source.FromVariationTreeSource;
-import org.variantsync.diffdetective.variation.tree.source.LocalFileSource;
-import org.variantsync.diffdetective.variation.tree.source.VariationTreeSource;
import java.io.BufferedReader;
import java.io.IOException;
@@ -22,6 +22,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -42,15 +43,15 @@
*/
public record VariationTree(
VariationTreeNode root,
- VariationTreeSource source
-) {
+ Source source
+) implements Source {
/** Creates a {@code VariationTree} with the given root and an unknown source. */
public VariationTree(VariationTreeNode root) {
- this(root, VariationTreeSource.Unknown);
+ this(root, Source.Unknown);
}
/** Creates a {@code VariationTree} with the given root and source. */
- public VariationTree(VariationTreeNode root, VariationTreeSource source) {
+ public VariationTree(VariationTreeNode root, Source source) {
this.root = root;
this.source = source;
@@ -66,7 +67,7 @@ public static VariationTree fromFile(final Path path) throws IOE
}
/**
- * Same as {@link #fromFile(BufferedReader, VariationTreeSource, VariationDiffParseOptions)}
+ * Same as {@link #fromFile(BufferedReader, Source, VariationDiffParseOptions)}
* but registers {@code path} as source.
*/
public static VariationTree fromFile(
@@ -76,8 +77,8 @@ public static VariationTree fromFile(
try (BufferedReader file = Files.newBufferedReader(path)) {
return fromFile(
file,
- new LocalFileSource(path),
- parseOptions
+ new FileSource(path),
+ parseOptions
);
}
}
@@ -94,17 +95,16 @@ public static VariationTree fromFile(
*/
public static VariationTree fromFile(
final BufferedReader input,
- final VariationTreeSource source,
+ final Source source,
final VariationDiffParseOptions parseOptions
) throws IOException, DiffParseException {
- VariationTreeNode tree = VariationDiffParser
- .createVariationTree(input, parseOptions)
- .getRoot()
- // Arbitrarily choose the BEFORE projection as both should be equal.
- .projection(BEFORE)
- .toVariationTree();
+ VariationDiff diff =
+ VariationDiffParser.createVariationTree(input, source, parseOptions);
- return new VariationTree<>(tree, source);
+ return new VariationTree<>(
+ // Arbitrarily choose the BEFORE projection as both should be equal.
+ diff.getRoot().projection(BEFORE).toVariationTree(),
+ diff.getSource());
}
/**
@@ -118,7 +118,7 @@ public static VariationTree fromFile(
*/
public static VariationTree fromText(
final String input,
- final VariationTreeSource source,
+ final Source source,
final VariationDiffParseOptions parseOptions
) throws DiffParseException {
try {
@@ -129,11 +129,11 @@ public static VariationTree fromText(
}
}
- public static VariationTree fromProjection(final Projection projection, final VariationTreeSource source) {
+ public static VariationTree fromProjection(final Projection projection, final Source source) {
return fromVariationNode(projection, source);
}
- public static , L extends Label> VariationTree fromVariationNode(final VariationNode node, final VariationTreeSource source) {
+ public static , L extends Label> VariationTree fromVariationNode(final VariationNode node, final Source source) {
return new VariationTree<>(
node.toVariationTree(),
source
@@ -143,7 +143,7 @@ public static , L extends Label> VariationTree
public VariationDiff toVariationDiff(final Function, DiffNode> nodeConverter) {
return new VariationDiff<>(
DiffNode.unchanged(nodeConverter, root()),
- new FromVariationTreeSource(source())
+ new CompositeSource("VariationTree.toVariationDiff", source())
);
}
@@ -225,6 +225,20 @@ public void assertConsistency() {
forAllPreorder(VariationTreeNode::assertConsistency);
}
+ public Source getSource() {
+ return source();
+ }
+
+ @Override
+ public List getSources() {
+ return List.of(source);
+ }
+
+ @Override
+ public String getSourceExplanation() {
+ return "VariationTree";
+ }
+
@Override
public String toString() {
return "variation tree from " + source;
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/source/GitSource.java b/src/main/java/org/variantsync/diffdetective/variation/tree/source/GitSource.java
index d79b70eec..9722d4418 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/source/GitSource.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/source/GitSource.java
@@ -1,7 +1,10 @@
package org.variantsync.diffdetective.variation.tree.source;
-import java.net.URL;
import java.nio.file.Path;
+import java.util.List;
+
+import org.variantsync.diffdetective.datasets.Repository;
+import org.variantsync.diffdetective.util.Source;
/**
* A file at a specific commit in a Git repository.
@@ -9,19 +12,29 @@
* The parameters of this record should be suitably chosen, so that the following commands can be
* executed in a shell to obtain the referenced source code:
*
- * git clone "$repository" repository
+ * git clone "${repository.getRemoteURI()}" repository
* cd repository
* git switch -d "$commitHash"
* cat "$path"
*
*/
public record GitSource(
- URL repository,
+ Repository repository,
String commitHash,
Path path
-) implements VariationTreeSource {
+) implements Source {
@Override
public String toString() {
return path.toString() + " at " + commitHash + " of " + repository;
}
+
+ @Override
+ public String getSourceExplanation() {
+ return "GitCommit";
+ }
+
+ @Override
+ public List getSourceArguments() {
+ return List.of(repository, commitHash, path);
+ }
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/source/LocalFileSource.java b/src/main/java/org/variantsync/diffdetective/variation/tree/source/LocalFileSource.java
deleted file mode 100644
index c9c60fbb9..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/source/LocalFileSource.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.variantsync.diffdetective.variation.tree.source;
-
-import java.nio.file.Path;
-
-/**
- * A reference to a file with path {@code path} in the local file system.
- */
-public record LocalFileSource(Path path) implements VariationTreeSource {
- @Override
- public String toString() {
- return "file://" + path.toString();
- }
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/source/VariationTreeSource.java b/src/main/java/org/variantsync/diffdetective/variation/tree/source/VariationTreeSource.java
deleted file mode 100644
index 448ac1511..000000000
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/source/VariationTreeSource.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.variantsync.diffdetective.variation.tree.source;
-
-/**
- * A reference to the source of the variation tree.
- */
-public interface VariationTreeSource {
- /**
- * The source of the variation tree is unknown.
- * Should be avoided if possible.
- */
- VariationTreeSource Unknown = new VariationTreeSource() {
- @Override
- public String toString() {
- return "unknown";
- }
- };
-
- String toString();
-}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Configure.java b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Configure.java
index 2b571b3f7..177007b5e 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Configure.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Configure.java
@@ -7,6 +7,7 @@
import org.variantsync.diffdetective.util.fide.FixTrueFalse.Formula;
import org.variantsync.diffdetective.variation.tree.VariationNode;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
@@ -94,12 +95,12 @@ public boolean test(VariationNode, ?> v) {
}
@Override
- public String parametersToString() {
- return configuration.get().toString(NodeWriter.logicalSymbols);
+ public List getSourceArguments() {
+ return List.of(configuration.get().toString(NodeWriter.logicalSymbols));
}
@Override
- public String getFunctionName() {
+ public String getSourceExplanation() {
return "configure";
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Relevance.java b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Relevance.java
index 8bc06b5e6..cd8097767 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Relevance.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Relevance.java
@@ -1,5 +1,6 @@
package org.variantsync.diffdetective.variation.tree.view.relevance;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.tree.VariationNode;
import java.util.function.Consumer;
@@ -13,17 +14,7 @@
* Moreover, this interface provides methods to access a predicates metadata for debugging
* and (de-)serialization.
*/
-public interface Relevance extends Predicate> {
- /**
- * @return The name of this relevance predicate's type.
- */
- String getFunctionName();
-
- /**
- * @return The parameters set for this particular relevance predicate, as a comma-separated string (without braces).
- */
- String parametersToString();
-
+public interface Relevance extends Predicate>, Source {
/**
* Delegates to {@link Relevance#computeViewNodesCheckAll(Relevance, VariationNode, Consumer)} with this relevance
* as the first parameter.
@@ -57,9 +48,9 @@ public interface Relevance extends Predicate> {
* Default implementation for {@link Object#toString()} that can be reused by implementing classes.
* The produced string will look like a function call.
* @param relevance The relevance predicate to turn into a string.
- * @return {@link Relevance#getFunctionName()} + "(" + {@link Relevance#parametersToString()} + ")"
+ * @return {@link #getSourceArguments()} + "(" + {@link #getSourceExplanation()} + ")"
*/
static String toString(Relevance relevance) {
- return relevance.getFunctionName() + "(" + relevance.parametersToString() + ")";
+ return relevance.shallowExplanation();
}
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Search.java b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Search.java
index 0f557e7c2..14448078a 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Search.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Search.java
@@ -1,5 +1,7 @@
package org.variantsync.diffdetective.variation.tree.view.relevance;
+import java.util.List;
+
import org.variantsync.diffdetective.variation.tree.VariationNode;
/**
@@ -17,12 +19,12 @@ public boolean test(VariationNode, ?> v) {
}
@Override
- public String parametersToString() {
- return artifact();
+ public List getSourceArguments() {
+ return List.of(artifact());
}
@Override
- public String getFunctionName() {
+ public String getSourceExplanation() {
return "is";
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Trace.java b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Trace.java
index d559605a4..ed0d33c35 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Trace.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/Trace.java
@@ -1,5 +1,7 @@
package org.variantsync.diffdetective.variation.tree.view.relevance;
+import java.util.List;
+
import org.variantsync.diffdetective.variation.tree.VariationNode;
/**
@@ -15,12 +17,12 @@ public boolean test(VariationNode, ?> v) {
}
@Override
- public String parametersToString() {
- return featureName();
+ public List getSourceArguments() {
+ return List.of(featureName());
}
@Override
- public String getFunctionName() {
+ public String getSourceExplanation() {
return "traceall";
}
diff --git a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/TraceSup.java b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/TraceSup.java
index d990256c1..bae357331 100644
--- a/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/TraceSup.java
+++ b/src/main/java/org/variantsync/diffdetective/variation/tree/view/relevance/TraceSup.java
@@ -1,5 +1,7 @@
package org.variantsync.diffdetective.variation.tree.view.relevance;
+import java.util.List;
+
import org.prop4j.Node;
import org.prop4j.NodeWriter;
import org.variantsync.diffdetective.analysis.logic.SAT;
@@ -16,12 +18,12 @@ public boolean test(VariationNode, ?> variationNode) {
}
@Override
- public String parametersToString() {
- return configuration.toString(NodeWriter.logicalSymbols);
+ public List getSourceArguments() {
+ return List.of(configuration.toString(NodeWriter.logicalSymbols));
}
@Override
- public String getFunctionName() {
+ public String getSourceExplanation() {
return "traceyes";
}
diff --git a/src/test/java/JPPParserTest.java b/src/test/java/JPPParserTest.java
index a2ac2dc8f..b73ec7ff3 100644
--- a/src/test/java/JPPParserTest.java
+++ b/src/test/java/JPPParserTest.java
@@ -7,6 +7,7 @@
import org.variantsync.diffdetective.feature.Annotation;
import org.variantsync.diffdetective.feature.AnnotationType;
import org.variantsync.diffdetective.feature.jpp.JPPAnnotationParser;
+import org.variantsync.diffdetective.util.FileSource;
import org.variantsync.diffdetective.util.IO;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
@@ -132,6 +133,7 @@ public void fullDiffTestCase(JPPParserTest.TestCase testCase) throws
try (var inputFile = Files.newBufferedReader(testCase.input)) {
variationDiff = VariationDiffParser.createVariationDiff(
inputFile,
+ new FileSource(testCase.input),
new VariationDiffParseOptions(
false,
false
diff --git a/src/test/java/TreeDiffingTest.java b/src/test/java/TreeDiffingTest.java
index 1d3dd395d..24d9130e9 100644
--- a/src/test/java/TreeDiffingTest.java
+++ b/src/test/java/TreeDiffingTest.java
@@ -5,22 +5,21 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.variantsync.diffdetective.diff.result.DiffParseException;
+import org.variantsync.diffdetective.util.FileSource;
import org.variantsync.diffdetective.util.IO;
+import org.variantsync.diffdetective.util.Source;
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.diff.DiffNode;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.construction.GumTreeDiff;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
-import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParser;
import org.variantsync.diffdetective.variation.diff.serialize.Format;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphExporter;
import org.variantsync.diffdetective.variation.diff.serialize.TikzExporter;
import org.variantsync.diffdetective.variation.diff.serialize.edgeformat.ChildOrderEdgeFormat;
import org.variantsync.diffdetective.variation.diff.serialize.edgeformat.DefaultEdgeLabelFormat;
import org.variantsync.diffdetective.variation.diff.serialize.nodeformat.FullNodeFormat;
-import org.variantsync.diffdetective.variation.diff.source.VariationDiffSource;
import org.variantsync.diffdetective.variation.tree.VariationTree;
-import org.variantsync.diffdetective.variation.tree.source.LocalFileSource;
import java.io.IOException;
import java.nio.file.Files;
@@ -29,7 +28,6 @@
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.fail;
-import static org.variantsync.diffdetective.variation.diff.Time.BEFORE;
public class TreeDiffingTest {
private final static Path testDir = Constants.RESOURCE_DIR.resolve("tree-diffing");
@@ -83,8 +81,8 @@ private static Stream createMatchingTestCases() throws IOException {
@ParameterizedTest
@MethodSource("createMatchingTestCases")
public void createMatchingTestCase(TestCase testCase) throws IOException, DiffParseException {
- VariationTree beforeEdit = parseVariationTree(testCase.beforeEdit());
- VariationTree afterEdit = parseVariationTree(testCase.afterEdit());
+ VariationTree beforeEdit = VariationTree.fromFile(testCase.beforeEdit());
+ VariationTree afterEdit = VariationTree.fromFile(testCase.afterEdit());
assertExpectedVariationDiffs(testCase, GumTreeDiff.diffUsingMatching(beforeEdit, afterEdit, testCase.matcher()));
}
@@ -104,7 +102,7 @@ public void improveMatchingTestCase(TestCase testCase) throws IOException, DiffP
);
DiffNode improvedDiffNode = GumTreeDiff.improveMatching(variationDiff.getRoot(), testCase.matcher());
- VariationDiff improvedVariationDiff = new VariationDiff<>(improvedDiffNode, VariationDiffSource.Unknown);
+ VariationDiff improvedVariationDiff = new VariationDiff<>(improvedDiffNode, Source.Unknown);
assertExpectedVariationDiffs(testCase, improvedVariationDiff);
}
@@ -139,16 +137,4 @@ private static void assertExpectedVariationDiffs(TestCase testCase, VariationDif
}
}
}
-
- private static VariationTree parseVariationTree(Path filename) throws IOException, DiffParseException {
- try (var file = Files.newBufferedReader(filename)) {
- return new VariationTree<>(
- VariationDiffParser.createVariationTree(
- file,
- VariationDiffParseOptions.Default
- ).getRoot().projection(BEFORE).toVariationTree(),
- new LocalFileSource(filename)
- );
- }
- }
}
diff --git a/src/test/java/VariationDiffParserTest.java b/src/test/java/VariationDiffParserTest.java
index 4c1d4e48f..7646842bf 100644
--- a/src/test/java/VariationDiffParserTest.java
+++ b/src/test/java/VariationDiffParserTest.java
@@ -7,7 +7,6 @@
import org.variantsync.diffdetective.variation.DiffLinesLabel;
import org.variantsync.diffdetective.variation.diff.VariationDiff;
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
-import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParser;
import org.variantsync.diffdetective.variation.diff.serialize.Format;
import org.variantsync.diffdetective.variation.diff.serialize.LineGraphExporter;
import org.variantsync.diffdetective.variation.diff.serialize.TikzExporter;
@@ -60,15 +59,13 @@ public static void testCase(Path testCasePath) throws IOException, DiffParseExce
var expectedPath = testCasePath.getParent().resolve(basename + "_expected.lg");
VariationDiff variationDiff;
- try (var inputFile = Files.newBufferedReader(testCasePath)) {
- variationDiff = VariationDiffParser.createVariationDiff(
- inputFile,
- new VariationDiffParseOptions(
- false,
- false
- )
- );
- }
+ variationDiff = VariationDiff.fromFile(
+ testCasePath,
+ new VariationDiffParseOptions(
+ false,
+ false
+ )
+ );
try (var output = IO.newBufferedOutputStream(actualPath)) {
new LineGraphExporter<>(new Format<>(new FullNodeFormat(), new ChildOrderEdgeFormat<>()))