From cc340e8c3c3c9912dec7b0f977c2ed57ec30c749 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Sat, 24 Sep 2022 17:50:28 +0200
Subject: [PATCH 1/9] Strip `DiffTree` from `DiffTreeLineGraphExport` names

---
 .../analysis/CommitHistoryAnalysisTask.java   |  6 ++---
 .../analysis/HistoryAnalysis.java             |  4 +--
 .../analysis/strategies/AnalysisMonitor.java  |  4 +--
 .../analysis/strategies/AnalysisStrategy.java |  6 ++---
 .../strategies/AnalyzeAllThenExport.java      |  4 +--
 .../AnalyzeAndExportIncrementally.java        |  4 +--
 .../strategies/CompositeAnalysisStrategy.java |  4 +--
 .../difftree/render/DiffTreeRenderer.java     | 26 +++++++++----------
 .../difftree/render/PatchDiffRenderer.java    |  4 +--
 .../diff/difftree/render/RenderOptions.java   |  6 ++---
 .../difftree/serialize/LineGraphExport.java   | 24 ++++++++---------
 ...tions.java => LineGraphExportOptions.java} |  8 +++---
 ...phExporter.java => LineGraphExporter.java} |  6 ++---
 .../difftree/serialize/LineGraphImport.java   | 10 +++----
 ...tions.java => LineGraphImportOptions.java} |  2 +-
 .../diffdetective/mining/DiffTreeMiner.java   | 12 ++++-----
 .../diffdetective/mining/MiningTask.java      |  4 +--
 .../postprocessing/MiningPostprocessing.java  |  4 +--
 .../validation/EditClassValidationTask.java   |  4 +--
 .../diffdetective/validation/Validation.java  | 12 ++++-----
 src/test/java/ExportTest.java                 |  4 +--
 src/test/java/LineGraphTest.java              |  4 +--
 src/test/java/MarlinDebug.java                |  2 +-
 src/test/java/TestMultiLineMacros.java        |  6 ++---
 24 files changed, 85 insertions(+), 85 deletions(-)
 rename src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/{DiffTreeLineGraphExportOptions.java => LineGraphExportOptions.java} (88%)
 rename src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/{DiffTreeLineGraphExporter.java => LineGraphExporter.java} (91%)
 rename src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/{DiffTreeLineGraphImportOptions.java => LineGraphImportOptions.java} (95%)

diff --git a/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java b/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
index 66d731886..700dff054 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
@@ -5,7 +5,7 @@
 import org.variantsync.diffdetective.analysis.strategies.AnalysisStrategy;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.GitDiffer;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.util.CSV;
 import org.variantsync.diffdetective.util.IO;
 import org.variantsync.diffdetective.util.StringUtils;
@@ -37,7 +37,7 @@ public record Options(
         Repository repository,
         GitDiffer differ,
         Path outputDir,
-        DiffTreeLineGraphExportOptions exportOptions,
+        LineGraphExportOptions exportOptions,
         AnalysisStrategy analysisStrategy,
         Iterable<RevCommit> commits
     ) {}
@@ -61,7 +61,7 @@ public AnalysisResult call() throws Exception {
         options.analysisStrategy().start(options.repository(), options.outputDir(), options.exportOptions());
 
         final AnalysisResult miningResult = new AnalysisResult(options.repository.getRepositoryName());
-        final DiffTreeLineGraphExportOptions exportOptions = options.exportOptions();
+        final LineGraphExportOptions exportOptions = options.exportOptions();
 
         miningResult.putCustomInfo(MetadataKeys.TREEFORMAT, exportOptions.treeFormat().getName());
         miningResult.putCustomInfo(MetadataKeys.NODEFORMAT, exportOptions.nodeFormat().getName());
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java b/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
index 3d44e79c3..518dca04a 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
@@ -6,7 +6,7 @@
 import org.variantsync.diffdetective.analysis.strategies.AnalysisStrategy;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.GitDiffer;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.metadata.Metadata;
 import org.variantsync.diffdetective.mining.MiningTask;
 import org.variantsync.diffdetective.parallel.ScheduledTasksIterator;
@@ -56,7 +56,7 @@ public record HistoryAnalysis(
     public static void analyze(
             final Repository repo,
             final Path outputDir,
-            final DiffTreeLineGraphExportOptions exportOptions,
+            final LineGraphExportOptions exportOptions,
             final AnalysisStrategy strategy)
     {
         AnalysisResult totalResult;
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
index eb48d3763..98556e9b8 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
@@ -3,7 +3,7 @@
 import org.variantsync.diffdetective.analysis.monitoring.TaskCompletionMonitor;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 
 import java.nio.file.Path;
 
@@ -24,7 +24,7 @@ public AnalysisMonitor(int seconds) {
     }
 
     @Override
-    public void start(Repository repo, Path outputPath, DiffTreeLineGraphExportOptions options) {
+    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
         super.start(repo, outputPath, options);
         monitor.start();
     }
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
index eaf854143..83ac02e8b 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
@@ -2,7 +2,7 @@
 
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 
 import java.nio.file.Path;
 
@@ -15,7 +15,7 @@
 public abstract class AnalysisStrategy {
     protected Repository repo;
     protected Path outputPath;
-    protected DiffTreeLineGraphExportOptions exportOptions;
+    protected LineGraphExportOptions exportOptions;
 
     /**
      * Invoked when the analysis starts.
@@ -24,7 +24,7 @@ public abstract class AnalysisStrategy {
      * @param outputPath A directory to which output should be written.
      * @param options Options for data export.
      */
-    public void start(Repository repo, Path outputPath, DiffTreeLineGraphExportOptions options) {
+    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
         this.repo = repo;
         this.outputPath = outputPath;
         this.exportOptions = options;
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
index 7b222f0ce..61427c46d 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
@@ -2,7 +2,7 @@
 
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.util.IO;
 
 import java.nio.file.Path;
@@ -15,7 +15,7 @@ public class AnalyzeAllThenExport extends AnalysisStrategy {
     private StringBuilder waitForAll;
 
     @Override
-    public void start(Repository repo, Path outputPath, DiffTreeLineGraphExportOptions options) {
+    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
         super.start(repo, outputPath, options);
         waitForAll = new StringBuilder();
     }
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
index 1f70a49c2..4a3a6202c 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
@@ -3,7 +3,7 @@
 import org.tinylog.Logger;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.util.IO;
 
 import java.io.IOException;
@@ -44,7 +44,7 @@ public AnalyzeAndExportIncrementally() {
     }
 
     @Override
-    public void start(Repository repo, Path outputPath, DiffTreeLineGraphExportOptions options) {
+    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
         super.start(repo, outputPath, options);
 
         IO.tryDeleteFile(outputPath);
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
index 482336135..dddbb5a17 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
@@ -2,7 +2,7 @@
 
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -26,7 +26,7 @@ public CompositeAnalysisStrategy(final AnalysisStrategy... strategies) {
     }
 
     @Override
-    public void start(Repository repo, Path outputPath, DiffTreeLineGraphExportOptions options) {
+    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
         super.start(repo, outputPath, options);
         for (final AnalysisStrategy s : strategies) {
             s.start(repo, outputPath, options);
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
index 80c455d59..4592c3880 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
@@ -6,7 +6,7 @@
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.DiffTreeSource;
 import org.variantsync.diffdetective.diff.difftree.LineGraphConstants;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeSerializeDebugData;
 import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExport;
 import org.variantsync.diffdetective.shell.PythonCommand;
@@ -103,27 +103,27 @@ public boolean render(PatchDiff patchDiff, final Path directory, final RenderOpt
     }
 
     /**
-     * Invokes {@link DiffTreeRenderer#render(DiffTree, GitPatch, Path, RenderOptions, DiffTreeLineGraphExportOptions)}
+     * Invokes {@link DiffTreeRenderer#render(DiffTree, GitPatch, Path, RenderOptions, LineGraphExportOptions)}
      * by extracting the given patchDiff's DiffTree.
      */
-    public boolean render(PatchDiff patchDiff, final Path directory, final RenderOptions options, final DiffTreeLineGraphExportOptions exportOptions) {
+    public boolean render(PatchDiff patchDiff, final Path directory, final RenderOptions options, final LineGraphExportOptions exportOptions) {
         return render(patchDiff.getDiffTree(), patchDiff, directory, options, exportOptions);
     }
 
     /**
-     * Invokes {@link DiffTreeRenderer#render(DiffTree, GitPatch, Path, RenderOptions, DiffTreeLineGraphExportOptions)}
-     * by creating {@link DiffTreeLineGraphExportOptions} via {@link RenderOptions#toLineGraphOptions()}.
+     * Invokes {@link DiffTreeRenderer#render(DiffTree, GitPatch, Path, RenderOptions, LineGraphExportOptions)}
+     * by creating {@link LineGraphExportOptions} via {@link RenderOptions#toLineGraphOptions()}.
      */
     public boolean render(final DiffTree tree, final GitPatch patch, final Path directory, final RenderOptions options) {
         return render(tree, patch, directory, options, options.toLineGraphOptions());
     }
 
     /**
-     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, DiffTreeLineGraphExportOptions)}
+     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, LineGraphExportOptions)}
      * by creating a name for the tree and its produced image file.
      * The created <code>treeAndFileName</code> is the given patches file name and commit hash, separated by {@link LineGraphConstants#TREE_NAME_SEPARATOR}.
      */
-    public boolean render(final DiffTree tree, final GitPatch patch, final Path directory, final RenderOptions options, final DiffTreeLineGraphExportOptions exportOptions) {
+    public boolean render(final DiffTree tree, final GitPatch patch, final Path directory, final RenderOptions options, final LineGraphExportOptions exportOptions) {
         final String treeAndFileName =
                 patch.getFileName()
                         + LineGraphConstants.TREE_NAME_SEPARATOR
@@ -140,29 +140,29 @@ public boolean render(final DiffTree tree, final String treeAndFileName, final P
     }
 
     /**
-     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, DiffTreeLineGraphExportOptions)}
-     * by creating {@link DiffTreeLineGraphExportOptions} via {@link RenderOptions#toLineGraphOptions()}.
+     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, LineGraphExportOptions)}
+     * by creating {@link LineGraphExportOptions} via {@link RenderOptions#toLineGraphOptions()}.
      */
     public boolean render(final DiffTree tree, final String treeAndFileName, final Path directory, RenderOptions options) {
         return render(tree, treeAndFileName, directory, options, options.toLineGraphOptions());
     }
 
     /**
-     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, DiffTreeLineGraphExportOptions, BiFunction)}
+     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, LineGraphExportOptions, BiFunction)}
      * with the a default tree header factory.
      * The tree header factory uses the given <code>treeAndFileName</code> to create
      * <code>LineGraphConstants.LG_TREE_HEADER + " " + treeAndFileName + LineGraphConstants.TREE_NAME_SEPARATOR + "0"</code>.
      * @see LineGraphConstants#LG_TREE_HEADER
      * @see LineGraphConstants#TREE_NAME_SEPARATOR
      */
-    public boolean render(final DiffTree tree, final String treeAndFileName, final Path directory, RenderOptions options, final DiffTreeLineGraphExportOptions exportOptions) {
+    public boolean render(final DiffTree tree, final String treeAndFileName, final Path directory, RenderOptions options, final LineGraphExportOptions exportOptions) {
         return render(tree, treeAndFileName, directory, options, exportOptions,
                 (treeName, treeSource) -> LineGraphConstants.LG_TREE_HEADER + " " + treeAndFileName + LineGraphConstants.TREE_NAME_SEPARATOR + "0"
                 );
     }
 
     /**
-     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, DiffTreeLineGraphExportOptions, BiFunction)}
+     * Invokes {@link DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, LineGraphExportOptions, BiFunction)}
      * with the a default tree header factory.
      * The tree header factory uses the given <code>treeAndFileName</code> to create
      * <code>LineGraphConstants.LG_TREE_HEADER + " " + treeAndFileName + LineGraphConstants.TREE_NAME_SEPARATOR + "0"</code>.
@@ -191,7 +191,7 @@ public boolean renderWithTreeFormat(final DiffTree tree, final String treeAndFil
      *                   The function is invoked on the given treeAndFileName as first argument and the given DiffTree's source as second argument.
      * @return True iff rendering was successful. False iff an error occurred.
      */
-    private boolean render(final DiffTree tree, final String treeAndFileName, final Path directory, RenderOptions options, DiffTreeLineGraphExportOptions exportOptions, BiFunction<String, DiffTreeSource, String> treeHeader) {
+    private boolean render(final DiffTree tree, final String treeAndFileName, final Path directory, RenderOptions options, LineGraphExportOptions exportOptions, BiFunction<String, DiffTreeSource, String> treeHeader) {
         final Path tempFile = directory.resolve(treeAndFileName + ".lg");
 
         final Pair<DiffTreeSerializeDebugData, String> result = LineGraphExport.toLineGraphFormat(tree, exportOptions);
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/PatchDiffRenderer.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/PatchDiffRenderer.java
index 2864d6f5e..20eeb61b0 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/PatchDiffRenderer.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/PatchDiffRenderer.java
@@ -5,7 +5,7 @@
 import org.variantsync.diffdetective.diff.PatchDiff;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.LineGraphConstants;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.nodeformat.TypeDiffNodeFormat;
 import org.variantsync.diffdetective.util.IO;
 
@@ -64,7 +64,7 @@ public static PatchDiffRenderer ErrorRendering(final DiffTreeRenderer renderer)
      * Renders the given patch to the given output directory.
      * @param patch The patch to render.
      * @param outputDirectory The directory to which the rendered image should be written.
-     * @see DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, DiffTreeLineGraphExportOptions, BiFunction)
+     * @see DiffTreeRenderer#render(DiffTree, String, Path, RenderOptions, LineGraphExportOptions, BiFunction)
      */
     public void render(final PatchDiff patch, final Path outputDirectory) {
         render(patch.getDiffTree(), patch, outputDirectory);
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/RenderOptions.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/RenderOptions.java
index 36fdd3e50..a5eb5c56a 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/RenderOptions.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/RenderOptions.java
@@ -1,6 +1,6 @@
 package org.variantsync.diffdetective.diff.difftree.render;
 
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.GraphFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.DefaultEdgeLabelFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat;
@@ -221,7 +221,7 @@ public Builder addExtraArguments(String... args) {
 	 * Linegraph options are a subset of render options.
 	 * @return Options for linegraph export consistent to this RenderOptions.
 	 */
-	public DiffTreeLineGraphExportOptions toLineGraphOptions() {
-		return new DiffTreeLineGraphExportOptions(format(), treeFormat(), nodeFormat(), edgeFormat());
+	public LineGraphExportOptions toLineGraphOptions() {
+		return new LineGraphExportOptions(format(), treeFormat(), nodeFormat(), edgeFormat());
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
index 1abb8c01d..de89f2afc 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
@@ -26,12 +26,12 @@ private LineGraphExport() {}
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @return A pair holding some debug information and the produced linegraph as a string.
      */
-    public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final DiffTree diffTree, final DiffTreeLineGraphExportOptions options) {
+    public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final DiffTree diffTree, final LineGraphExportOptions options) {
         DiffTreeTransformer.apply(options.treePreProcessing(), diffTree);
         diffTree.assertConsistency();
 
         if (options.treeFilter().test(diffTree)) {
-            final var exporter = new DiffTreeLineGraphExporter(options);
+            final var exporter = new LineGraphExporter(options);
             var output = new ByteArrayOutputStream();
             exporter.exportDiffTree(diffTree, output);
             return new Pair<>(exporter.getDebugData(), output.toString());
@@ -47,7 +47,7 @@ public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final D
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @return A pair of (1) metadata about the exported DiffTrees, and (2) the produced linegraph as String.
      */
-    public static Pair<AnalysisResult, String> toLineGraphFormat(final String repoName, final Iterable<DiffTree> trees, final DiffTreeLineGraphExportOptions options) {
+    public static Pair<AnalysisResult, String> toLineGraphFormat(final String repoName, final Iterable<DiffTree> trees, final LineGraphExportOptions options) {
         final AnalysisResult result = new AnalysisResult(repoName);
 
         final StringBuilder lineGraph = new StringBuilder();
@@ -68,18 +68,18 @@ public static Pair<AnalysisResult, String> toLineGraphFormat(final String repoNa
     }
 
     /**
-     * Same as {@link LineGraphExport#toLineGraphFormat(String, Iterable, DiffTreeLineGraphExportOptions)} but with an
+     * Same as {@link LineGraphExport#toLineGraphFormat(String, Iterable, LineGraphExportOptions)} but with an
      * {@link AnalysisResult#NO_REPO unkown repository}.
      */
-    public static Pair<AnalysisResult, String> toLineGraphFormat(final Iterable<DiffTree> trees, final DiffTreeLineGraphExportOptions options) {
+    public static Pair<AnalysisResult, String> toLineGraphFormat(final Iterable<DiffTree> trees, final LineGraphExportOptions options) {
         return toLineGraphFormat(AnalysisResult.NO_REPO, trees, options);
     }
 
     /**
-     * Same as {@link LineGraphExport#toLineGraphFormat(String, CommitDiff, StringBuilder, DiffTreeLineGraphExportOptions)}
+     * Same as {@link LineGraphExport#toLineGraphFormat(String, CommitDiff, StringBuilder, LineGraphExportOptions)}
      * but with an {@link AnalysisResult#NO_REPO unkown repository}.
      */
-    public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, final StringBuilder lineGraph, final DiffTreeLineGraphExportOptions options) {
+    public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, final StringBuilder lineGraph, final LineGraphExportOptions options) {
         return toLineGraphFormat(AnalysisResult.NO_REPO, commitDiff, lineGraph, options);
     }
 
@@ -91,7 +91,7 @@ public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, fina
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @return The number of the next diff tree to export (updated value of treeCounter).
      */
-    public static AnalysisResult toLineGraphFormat(final String repoName, final CommitDiff commitDiff, final StringBuilder lineGraph, final DiffTreeLineGraphExportOptions options) {
+    public static AnalysisResult toLineGraphFormat(final String repoName, final CommitDiff commitDiff, final StringBuilder lineGraph, final LineGraphExportOptions options) {
         final AnalysisResult result = new AnalysisResult(repoName);
 
         final String hash = commitDiff.getCommitHash();
@@ -124,14 +124,14 @@ public static AnalysisResult toLineGraphFormat(final String repoName, final Comm
     
     /**
      * Produces the final linegraph file content.
-     * Creates a linegraph header from the given DiffTreeSource using the {@link DiffTreeLineGraphExportOptions#treeFormat} in the given options.
+     * Creates a linegraph header from the given DiffTreeSource using the {@link LineGraphExportOptions#treeFormat} in the given options.
      * Then appends the already created file content for nodes and edges.
      * @param lineGraph The string builder to write the result to.
      * @param source The {@link DiffTreeSource} that describes where the DiffTree whose content is written to the file originated from.
-     * @param nodesAndEdges Result from {@link #toLineGraphFormat(DiffTree, DiffTreeLineGraphExportOptions)}. Holds all nodes and edges in linegraph format, separated by a newline each.
-     * @param options {@link DiffTreeLineGraphExportOptions} used to determine the treeFormat for the header.
+     * @param nodesAndEdges Result from {@link #toLineGraphFormat(DiffTree, LineGraphExportOptions)}. Holds all nodes and edges in linegraph format, separated by a newline each.
+     * @param options {@link LineGraphExportOptions} used to determine the treeFormat for the header.
      */
-    public static void composeTreeInLineGraph(final StringBuilder lineGraph, final DiffTreeSource source, final String nodesAndEdges, final DiffTreeLineGraphExportOptions options) {
+    public static void composeTreeInLineGraph(final StringBuilder lineGraph, final DiffTreeSource source, final String nodesAndEdges, final LineGraphExportOptions options) {
     	lineGraph
     		.append(options.treeFormat().toLineGraphLine(source)) // print "t # $LABEL"
     		.append(StringUtils.LINEBREAK)
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExportOptions.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
similarity index 88%
rename from src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExportOptions.java
rename to src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
index 1a1f519c1..57f3474bd 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExportOptions.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
@@ -29,7 +29,7 @@
  * @param onError Callback that is invoked when an error occurs.
  * @author Paul Bittner
  */
-public record DiffTreeLineGraphExportOptions(
+public record LineGraphExportOptions(
         GraphFormat graphFormat,
 		DiffTreeLabelFormat treeFormat,
 		DiffNodeLabelFormat nodeFormat,
@@ -41,17 +41,17 @@ public record DiffTreeLineGraphExportOptions(
     /**
      * Creates a export options with a neutral filter (that accepts all trees), no transformers, and that logs errors.
      */
-    public DiffTreeLineGraphExportOptions(GraphFormat graphFormat, DiffTreeLabelFormat treeFormat, DiffNodeLabelFormat nodeFormat, EdgeLabelFormat edgeFormat) {
+    public LineGraphExportOptions(GraphFormat graphFormat, DiffTreeLabelFormat treeFormat, DiffNodeLabelFormat nodeFormat, EdgeLabelFormat edgeFormat) {
         this(graphFormat, treeFormat, nodeFormat, edgeFormat, ExplainedFilter.Any(), new ArrayList<>(), LogError());
     }
 
     /**
      * Create export options from the given import options.
-     * Invokes {@link DiffTreeLineGraphExportOptions#DiffTreeLineGraphExportOptions(GraphFormat, DiffTreeLabelFormat, DiffNodeLabelFormat, EdgeLabelFormat)}
+     * Invokes {@link LineGraphExportOptions#LineGraphExportOptions(GraphFormat, DiffTreeLabelFormat, DiffNodeLabelFormat, EdgeLabelFormat)}
      * with all formats from the given import options.
      * @param importOptions The import options to convert to export options.
      */
-    public DiffTreeLineGraphExportOptions(final DiffTreeLineGraphImportOptions importOptions) {
+    public LineGraphExportOptions(final LineGraphImportOptions importOptions) {
         this(
                 importOptions.graphFormat(),
                 importOptions.treeFormat(),
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExporter.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExporter.java
similarity index 91%
rename from src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExporter.java
rename to src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExporter.java
index 411659a9f..073ee5461 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExporter.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExporter.java
@@ -10,16 +10,16 @@
 /**
  * Exporter that converts a single DiffTree's nodes and edges to linegraph.
  */
-public class DiffTreeLineGraphExporter implements Exporter {
+public class LineGraphExporter implements Exporter {
     private final Format format;
     private final DiffTreeSerializeDebugData debugData;
 
-    public DiffTreeLineGraphExporter(Format format) {
+    public LineGraphExporter(Format format) {
         this.format = format;
         this.debugData = new DiffTreeSerializeDebugData();
     }
 
-    public DiffTreeLineGraphExporter(DiffTreeLineGraphExportOptions options) {
+    public LineGraphExporter(LineGraphExportOptions options) {
         this(new Format(options.nodeFormat(), options.edgeFormat()));
     }
 
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImport.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImport.java
index 4e9cf7656..222f60ab0 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImport.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImport.java
@@ -27,9 +27,9 @@ public class LineGraphImport {
 	 * @param path Path to a linegraph file in which only DiffTrees are stored.
 	 * @param options Options for the import, such as hints for the used formats for node and edge labels.
      * @return All {@link DiffTree DiffTrees} contained in the linegraph file.
-	 * @throws IOException when {@link LineGraphImport#fromLineGraph(BufferedReader, Path, DiffTreeLineGraphImportOptions)} throws.
+	 * @throws IOException when {@link LineGraphImport#fromLineGraph(BufferedReader, Path, LineGraphImportOptions)} throws.
      */
-    public static List<DiffTree> fromFile(final Path path, final DiffTreeLineGraphImportOptions options) throws IOException {
+    public static List<DiffTree> fromFile(final Path path, final LineGraphImportOptions options) throws IOException {
         Assert.assertTrue(Files.isRegularFile(path));
         Assert.assertTrue(FileUtils.isLineGraph(path));
         try (BufferedReader input = Files.newBufferedReader(path)) {
@@ -45,7 +45,7 @@ public static List<DiffTree> fromFile(final Path path, final DiffTreeLineGraphIm
 	 * @param options Options for the import, such as hints for the used formats for node and edge labels.
 	 * @return All {@link DiffTree DiffTrees} contained in the linegraph text.
 	 */
-	public static List<DiffTree> fromLineGraph(final BufferedReader lineGraph, final Path originalFile, final DiffTreeLineGraphImportOptions options) throws IOException {
+	public static List<DiffTree> fromLineGraph(final BufferedReader lineGraph, final Path originalFile, final LineGraphImportOptions options) throws IOException {
 		// All DiffTrees read from the line graph
 		List<DiffTree> diffTreeList = new ArrayList<>();
 		
@@ -114,10 +114,10 @@ public static List<DiffTree> fromLineGraph(final BufferedReader lineGraph, final
 	 * @param lineGraph The header line in the linegraph that describes the DiffTree (starting with <code>t #</code>).
 	 * @param inFile Path to the linegraph file that is currently parsed.
 	 * @param diffNodeList All nodes of the DiffTree that is to be created. The nodes can be assumed to be complete and already connected.
-	 * @param options {@link DiffTreeLineGraphImportOptions}
+	 * @param options {@link LineGraphImportOptions}
 	 * @return {@link DiffTree} generated from the given, already parsed parameters.
 	 */
-	private static DiffTree parseDiffTree(final String lineGraph, final Path inFile, final List<DiffNode> diffNodeList, final DiffTreeLineGraphImportOptions options) {
+	private static DiffTree parseDiffTree(final String lineGraph, final Path inFile, final List<DiffNode> diffNodeList, final LineGraphImportOptions options) {
 		DiffTreeSource diffTreeSource = options.treeFormat().fromLineGraphLine(lineGraph);
 
 		if (diffTreeSource == null || DiffTreeSource.Unknown.equals(diffTreeSource)) {
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphImportOptions.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImportOptions.java
similarity index 95%
rename from src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphImportOptions.java
rename to src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImportOptions.java
index 44c3726a8..f17692145 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphImportOptions.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphImportOptions.java
@@ -13,7 +13,7 @@
  * @param nodeFormat {@link DiffNodeLabelFormat}
  * @param edgeFormat {@link EdgeLabelFormat}
  */
-public record DiffTreeLineGraphImportOptions(
+public record LineGraphImportOptions(
         GraphFormat graphFormat,
         DiffTreeLabelFormat treeFormat,
         DiffNodeLabelFormat nodeFormat,
diff --git a/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java b/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java
index eea3a1541..83240bdfd 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java
@@ -12,7 +12,7 @@
 import org.variantsync.diffdetective.diff.difftree.filter.DiffTreeFilter;
 import org.variantsync.diffdetective.diff.difftree.filter.ExplainedFilter;
 import org.variantsync.diffdetective.diff.difftree.parse.DiffNodeParser;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.GraphFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.treeformat.CommitDiffDiffTreeLabelFormat;
@@ -72,9 +72,9 @@ private static EdgeLabelFormat EdgeFormat(final MiningNodeFormat nodeFormat) {
                 new DirectedEdgeLabelFormat(nodeFormat, false, direction);
     }
 
-    public static DiffTreeLineGraphExportOptions MiningExportOptions(final Repository repository) {
+    public static LineGraphExportOptions MiningExportOptions(final Repository repository) {
         final MiningNodeFormat nodeFormat = NodeFormat();
-        return new DiffTreeLineGraphExportOptions(
+        return new LineGraphExportOptions(
                   GraphFormat.DIFFTREE
                 // We have to ensure that all DiffTrees have unique IDs, so use name of changed file and commit hash.
                 , new CommitDiffDiffTreeLabelFormat()
@@ -91,9 +91,9 @@ public static DiffTreeLineGraphExportOptions MiningExportOptions(final Repositor
                         DiffTreeFilter.hasAtLeastOneEditToVariability()
                 )
                 , Postprocessing(repository)
-                , DiffTreeLineGraphExportOptions.LogError()
-                .andThen(DiffTreeLineGraphExportOptions.RenderError())
-                .andThen(DiffTreeLineGraphExportOptions.SysExitOnError())
+                , LineGraphExportOptions.LogError()
+                .andThen(LineGraphExportOptions.RenderError())
+                .andThen(LineGraphExportOptions.SysExitOnError())
         );
     }
 
diff --git a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
index d20f18267..f882faea7 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
@@ -6,7 +6,7 @@
 import org.variantsync.diffdetective.diff.CommitDiff;
 import org.variantsync.diffdetective.diff.PatchDiff;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExport;
 import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.diff.result.CommitDiffResult;
@@ -27,7 +27,7 @@ public MiningTask(final Options options) {
     @Override
     public AnalysisResult call() throws Exception {
         final AnalysisResult miningResult = super.call();
-        final DiffTreeLineGraphExportOptions exportOptions = options.exportOptions();
+        final LineGraphExportOptions exportOptions = options.exportOptions();
 
         final Clock totalTime = new Clock();
 
diff --git a/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java b/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java
index 7549f240c..1ad3c512e 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java
@@ -26,13 +26,13 @@
 public class MiningPostprocessing {
     private static final DiffTreeRenderer DefaultRenderer = DiffTreeRenderer.WithinDiffDetective();
     private static final boolean RENDER_CANDIDATES = false;
-    private static final DiffTreeLineGraphImportOptions IMPORT_OPTIONS = new DiffTreeLineGraphImportOptions(
+    private static final LineGraphImportOptions IMPORT_OPTIONS = new LineGraphImportOptions(
             GraphFormat.DIFFGRAPH,
             new IndexedTreeFormat(),
             DiffTreeMiner.NodeFormat(),
             DiffTreeMiner.EdgeFormat()
     );
-    private static final DiffTreeLineGraphExportOptions EXPORT_OPTIONS = new DiffTreeLineGraphExportOptions(
+    private static final LineGraphExportOptions EXPORT_OPTIONS = new LineGraphExportOptions(
             GraphFormat.DIFFTREE,
             IMPORT_OPTIONS.treeFormat(),
             DiffTreeMiner.NodeFormat(),
diff --git a/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java b/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
index 38ce99370..4e71a46d5 100644
--- a/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
+++ b/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
@@ -9,7 +9,7 @@
 import org.variantsync.diffdetective.diff.CommitDiff;
 import org.variantsync.diffdetective.diff.PatchDiff;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.diff.result.CommitDiffResult;
 import org.variantsync.diffdetective.metadata.ExplainedFilterSummary;
@@ -33,7 +33,7 @@ public EditClassValidationTask(Options options) {
     public AnalysisResult call() throws Exception {
         // Setup. Obtain the result from the initial setup in the super class.
         final AnalysisResult miningResult = super.call();
-        final DiffTreeLineGraphExportOptions exportOptions = options.exportOptions();
+        final LineGraphExportOptions exportOptions = options.exportOptions();
         // List to store the process time of each commit.
         final List<CommitProcessTime> commitTimes = new ArrayList<>(HistoryAnalysis.COMMITS_TO_PROCESS_PER_THREAD_DEFAULT);
         // Clock for runtime measurement.
diff --git a/src/main/java/org/variantsync/diffdetective/validation/Validation.java b/src/main/java/org/variantsync/diffdetective/validation/Validation.java
index 431ac5aa4..2f5cf2144 100644
--- a/src/main/java/org/variantsync/diffdetective/validation/Validation.java
+++ b/src/main/java/org/variantsync/diffdetective/validation/Validation.java
@@ -10,7 +10,7 @@
 import org.variantsync.diffdetective.datasets.*;
 import org.variantsync.diffdetective.diff.difftree.filter.DiffTreeFilter;
 import org.variantsync.diffdetective.diff.difftree.filter.ExplainedFilter;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.GraphFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.treeformat.CommitDiffDiffTreeLabelFormat;
@@ -79,9 +79,9 @@ private static EdgeLabelFormat EdgeFormat(final MiningNodeFormat nodeFormat) {
     /**
      * Creates new export options for running the validation on the given repository.
      */
-    public static DiffTreeLineGraphExportOptions ValidationExportOptions(final Repository repository) {
+    public static LineGraphExportOptions ValidationExportOptions(final Repository repository) {
         final MiningNodeFormat nodeFormat = NodeFormat();
-        return new DiffTreeLineGraphExportOptions(
+        return new LineGraphExportOptions(
                 GraphFormat.DIFFTREE
                 // We have to ensure that all DiffTrees have unique IDs, so use name of changed file and commit hash.
                 , new CommitDiffDiffTreeLabelFormat()
@@ -89,9 +89,9 @@ public static DiffTreeLineGraphExportOptions ValidationExportOptions(final Repos
                 , EdgeFormat(nodeFormat)
                 , new ExplainedFilter<>(DiffTreeFilter.notEmpty())
                 , List.of(new CutNonEditedSubtrees())
-                , DiffTreeLineGraphExportOptions.LogError()
-                .andThen(DiffTreeLineGraphExportOptions.RenderError())
-                .andThen(DiffTreeLineGraphExportOptions.SysExitOnError())
+                , LineGraphExportOptions.LogError()
+                .andThen(LineGraphExportOptions.RenderError())
+                .andThen(LineGraphExportOptions.SysExitOnError())
         );
     }
 
diff --git a/src/test/java/ExportTest.java b/src/test/java/ExportTest.java
index 20fbebd96..75f4f916b 100644
--- a/src/test/java/ExportTest.java
+++ b/src/test/java/ExportTest.java
@@ -27,8 +27,8 @@ public class ExportTest {
     /**
      * Format used to deserialize the test case.
      */
-    private final static DiffTreeLineGraphImportOptions importOptions =
-        new DiffTreeLineGraphImportOptions(
+    private final static LineGraphImportOptions importOptions =
+        new LineGraphImportOptions(
             GraphFormat.DIFFTREE,
             new CommitDiffDiffTreeLabelFormat(),
             new LabelOnlyDiffNodeFormat(),
diff --git a/src/test/java/LineGraphTest.java b/src/test/java/LineGraphTest.java
index 6783bb867..5db6ddc2d 100644
--- a/src/test/java/LineGraphTest.java
+++ b/src/test/java/LineGraphTest.java
@@ -23,13 +23,13 @@
  * For testing the import of a line graph.
  */
 public class LineGraphTest {
-	private final static DiffTreeLineGraphImportOptions IMPORT_OPTIONS = new DiffTreeLineGraphImportOptions(
+	private final static LineGraphImportOptions IMPORT_OPTIONS = new LineGraphImportOptions(
             GraphFormat.DIFFTREE,
             new CommitDiffDiffTreeLabelFormat(),
             new LabelOnlyDiffNodeFormat(),
             new DefaultEdgeLabelFormat()
     );
-    private final static DiffTreeLineGraphExportOptions EXPORT_OPTIONS = new DiffTreeLineGraphExportOptions(
+    private final static LineGraphExportOptions EXPORT_OPTIONS = new LineGraphExportOptions(
             IMPORT_OPTIONS
     );
 
diff --git a/src/test/java/MarlinDebug.java b/src/test/java/MarlinDebug.java
index b2d0e4340..2673261bb 100644
--- a/src/test/java/MarlinDebug.java
+++ b/src/test/java/MarlinDebug.java
@@ -106,7 +106,7 @@ public static void testCommit(final RepoInspection repoInspection, final String
                         try {
                             Logger.info(ProposedEditClasses.Instance.match(node));
                         } catch (Exception e) {
-                            //DiffTreeLineGraphExportOptions.RenderError().accept(patch, e);
+                            //LineGraphExportOptions.RenderError().accept(patch, e);
                             Logger.error(e);
                             Logger.info("Died at node {}", node.toString());
                             Logger.info("  before parent: {}", node.getBeforeParent());
diff --git a/src/test/java/TestMultiLineMacros.java b/src/test/java/TestMultiLineMacros.java
index ccc39d663..e2ab633b7 100644
--- a/src/test/java/TestMultiLineMacros.java
+++ b/src/test/java/TestMultiLineMacros.java
@@ -4,7 +4,7 @@
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.parse.DiffNodeParser;
 import org.variantsync.diffdetective.diff.difftree.parse.DiffTreeParser;
-import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeLineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.serialize.DiffTreeSerializeDebugData;
 import org.variantsync.diffdetective.diff.difftree.serialize.GraphFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExport;
@@ -23,7 +23,7 @@
 public class TestMultiLineMacros {
     private static final Path resDir = Constants.RESOURCE_DIR.resolve("multilinemacros");
 
-    public void diffToDiffTree(DiffTreeLineGraphExportOptions exportOptions, Path p) throws IOException {
+    public void diffToDiffTree(LineGraphExportOptions exportOptions, Path p) throws IOException {
         DiffTree tree;
         try (BufferedReader fullDiff = Files.newBufferedReader(p)) {
             tree = DiffTreeParser.createDiffTree(
@@ -49,7 +49,7 @@ public void diffToDiffTree(DiffTreeLineGraphExportOptions exportOptions, Path p)
 
     @Test
     public void test() throws IOException {
-        final DiffTreeLineGraphExportOptions exportOptions = new DiffTreeLineGraphExportOptions(
+        final LineGraphExportOptions exportOptions = new LineGraphExportOptions(
                 GraphFormat.DIFFTREE,
                 new CommitDiffDiffTreeLabelFormat(),
                 new DebugDiffNodeFormat(),

From f87009b447a9c0f75c41cf6c975accd531c0ed8f Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Sun, 25 Sep 2022 09:55:17 +0200
Subject: [PATCH 2/9] Separate filtering and processing from exporting
 `DiffTree`s

Behavioural changes:

- `EditClassValidationTask` doesn't add export options to the metadata
  anymore because it doesn't actually export anything.

- Bug fix: The tree preprocessing in `MiningTask` was run twice:
  First for the line graph export and second directly by the `call`
  method.
---
 .../analysis/CommitHistoryAnalysisTask.java   | 17 ++---
 .../analysis/HistoryAnalysis.java             | 10 ++-
 .../analysis/strategies/AnalysisMonitor.java  |  5 +-
 .../analysis/strategies/AnalysisStrategy.java |  6 +-
 .../strategies/AnalyzeAllThenExport.java      |  5 +-
 .../AnalyzeAndExportIncrementally.java        |  5 +-
 .../strategies/CompositeAnalysisStrategy.java |  7 +-
 .../difftree/serialize/LineGraphExport.java   | 73 +++++++++----------
 .../serialize/LineGraphExportOptions.java     |  7 +-
 .../diffdetective/mining/DiffTreeMiner.java   | 25 +++----
 .../diffdetective/mining/MiningTask.java      | 32 +++++---
 .../validation/EditClassValidationTask.java   | 10 +--
 .../diffdetective/validation/Validation.java  |  5 +-
 src/test/java/MarlinDebug.java                | 16 ++--
 14 files changed, 106 insertions(+), 117 deletions(-)

diff --git a/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java b/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
index 700dff054..7bc86c676 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
@@ -5,7 +5,9 @@
 import org.variantsync.diffdetective.analysis.strategies.AnalysisStrategy;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.GitDiffer;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.DiffTree;
+import org.variantsync.diffdetective.diff.difftree.filter.ExplainedFilter;
+import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.util.CSV;
 import org.variantsync.diffdetective.util.IO;
 import org.variantsync.diffdetective.util.StringUtils;
@@ -29,7 +31,8 @@ public abstract class CommitHistoryAnalysisTask implements Callable<AnalysisResu
      * @param repository The repository that is analyzed.
      * @param differ The differ that should be used to obtain diffs.
      * @param outputDir The path to which any output should be written on disk.
-     * @param exportOptions Options for exporting DiffTrees.
+     * @param treeFilter filters commits before processing them
+     * @param treePreProcessing applies a processing function after filtering, but before processing
      * @param analysisStrategy A callback that is invoked for each commit.
      * @param commits The set of commits to process in this task.
      */
@@ -37,7 +40,8 @@ public record Options(
         Repository repository,
         GitDiffer differ,
         Path outputDir,
-        LineGraphExportOptions exportOptions,
+        ExplainedFilter<DiffTree> treeFilter,
+        List<DiffTreeTransformer> treePreProcessing,
         AnalysisStrategy analysisStrategy,
         Iterable<RevCommit> commits
     ) {}
@@ -58,14 +62,9 @@ public CommitHistoryAnalysisTask.Options getOptions() {
 
     @Override
     public AnalysisResult call() throws Exception {
-        options.analysisStrategy().start(options.repository(), options.outputDir(), options.exportOptions());
+        options.analysisStrategy().start(options.repository(), options.outputDir());
 
         final AnalysisResult miningResult = new AnalysisResult(options.repository.getRepositoryName());
-        final LineGraphExportOptions exportOptions = options.exportOptions();
-
-        miningResult.putCustomInfo(MetadataKeys.TREEFORMAT, exportOptions.treeFormat().getName());
-        miningResult.putCustomInfo(MetadataKeys.NODEFORMAT, exportOptions.nodeFormat().getName());
-        miningResult.putCustomInfo(MetadataKeys.EDGEFORMAT, exportOptions.edgeFormat().getName());
         miningResult.putCustomInfo(MetadataKeys.TASKNAME, this.getClass().getName());
 
         return miningResult;
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java b/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
index 518dca04a..92b7018c5 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
@@ -6,7 +6,10 @@
 import org.variantsync.diffdetective.analysis.strategies.AnalysisStrategy;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.GitDiffer;
+import org.variantsync.diffdetective.diff.difftree.DiffTree;
+import org.variantsync.diffdetective.diff.difftree.filter.ExplainedFilter;
 import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
+import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.metadata.Metadata;
 import org.variantsync.diffdetective.mining.MiningTask;
 import org.variantsync.diffdetective.parallel.ScheduledTasksIterator;
@@ -56,6 +59,8 @@ public record HistoryAnalysis(
     public static void analyze(
             final Repository repo,
             final Path outputDir,
+            final ExplainedFilter<DiffTree> treeFilter,
+            final List<DiffTreeTransformer> treePreProcessing,
             final LineGraphExportOptions exportOptions,
             final AnalysisStrategy strategy)
     {
@@ -71,10 +76,11 @@ public static void analyze(
                 repo,
                 differ,
                 outputDir.resolve(repo.getRepositoryName() + ".lg"),
-                exportOptions,
+                treeFilter,
+                treePreProcessing,
                 strategy,
                 commitsToProcess
-        ));
+        ), exportOptions);
         Logger.info("Scheduled {} commits.", commitsToProcess.size());
         commitsToProcess = null; // free reference to enable garbage collection
         Logger.info("<<< done after {}", clock.printPassedSeconds());
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
index 98556e9b8..860cd3817 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
@@ -3,7 +3,6 @@
 import org.variantsync.diffdetective.analysis.monitoring.TaskCompletionMonitor;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 
 import java.nio.file.Path;
 
@@ -24,8 +23,8 @@ public AnalysisMonitor(int seconds) {
     }
 
     @Override
-    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
-        super.start(repo, outputPath, options);
+    public void start(Repository repo, Path outputPath) {
+        super.start(repo, outputPath);
         monitor.start();
     }
 
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
index 83ac02e8b..8bec57262 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
@@ -2,7 +2,6 @@
 
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 
 import java.nio.file.Path;
 
@@ -15,19 +14,16 @@
 public abstract class AnalysisStrategy {
     protected Repository repo;
     protected Path outputPath;
-    protected LineGraphExportOptions exportOptions;
 
     /**
      * Invoked when the analysis starts.
      *
      * @param repo The repository on which an analysis is performed.
      * @param outputPath A directory to which output should be written.
-     * @param options Options for data export.
      */
-    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
+    public void start(Repository repo, Path outputPath) {
         this.repo = repo;
         this.outputPath = outputPath;
-        this.exportOptions = options;
     }
 
     /**
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
index 61427c46d..eb6405abf 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
@@ -2,7 +2,6 @@
 
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.util.IO;
 
 import java.nio.file.Path;
@@ -15,8 +14,8 @@ public class AnalyzeAllThenExport extends AnalysisStrategy {
     private StringBuilder waitForAll;
 
     @Override
-    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
-        super.start(repo, outputPath, options);
+    public void start(Repository repo, Path outputPath) {
+        super.start(repo, outputPath);
         waitForAll = new StringBuilder();
     }
 
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
index 4a3a6202c..1ddd341ad 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
@@ -3,7 +3,6 @@
 import org.tinylog.Logger;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.util.IO;
 
 import java.io.IOException;
@@ -44,8 +43,8 @@ public AnalyzeAndExportIncrementally() {
     }
 
     @Override
-    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
-        super.start(repo, outputPath, options);
+    public void start(Repository repo, Path outputPath) {
+        super.start(repo, outputPath);
 
         IO.tryDeleteFile(outputPath);
         nextChunkToExport = new StringBuilder();
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
index dddbb5a17..cba412c49 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
@@ -2,7 +2,6 @@
 
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -26,10 +25,10 @@ public CompositeAnalysisStrategy(final AnalysisStrategy... strategies) {
     }
 
     @Override
-    public void start(Repository repo, Path outputPath, LineGraphExportOptions options) {
-        super.start(repo, outputPath, options);
+    public void start(Repository repo, Path outputPath) {
+        super.start(repo, outputPath);
         for (final AnalysisStrategy s : strategies) {
-            s.start(repo, outputPath, options);
+            s.start(repo, outputPath);
         }
     }
 
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
index de89f2afc..a535b9d0e 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
@@ -8,8 +8,6 @@
 import org.variantsync.diffdetective.diff.PatchDiff;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.DiffTreeSource;
-import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
-import org.variantsync.diffdetective.metadata.ExplainedFilterSummary;
 import org.variantsync.diffdetective.util.StringUtils;
 import org.variantsync.functjonal.Pair;
 
@@ -27,17 +25,10 @@ private LineGraphExport() {}
      * @return A pair holding some debug information and the produced linegraph as a string.
      */
     public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final DiffTree diffTree, final LineGraphExportOptions options) {
-        DiffTreeTransformer.apply(options.treePreProcessing(), diffTree);
-        diffTree.assertConsistency();
-
-        if (options.treeFilter().test(diffTree)) {
-            final var exporter = new LineGraphExporter(options);
-            var output = new ByteArrayOutputStream();
-            exporter.exportDiffTree(diffTree, output);
-            return new Pair<>(exporter.getDebugData(), output.toString());
-        }
-
-        return null;
+        final var exporter = new LineGraphExporter(options);
+        var output = new ByteArrayOutputStream();
+        exporter.exportDiffTree(diffTree, output);
+        return new Pair<>(exporter.getDebugData(), output.toString());
     }
 
     /**
@@ -54,15 +45,12 @@ public static Pair<AnalysisResult, String> toLineGraphFormat(final String repoNa
         for (final DiffTree t : trees) {
             final Pair<DiffTreeSerializeDebugData, String> lg = toLineGraphFormat(t, options);
 
-            if (lg != null) {
-                result.debugData.append(lg.first());
-                composeTreeInLineGraph(lineGraph, t.getSource(), lg.second(), options);
-                ++result.exportedTrees;
-            }
+            result.debugData.append(lg.first());
+            composeTreeInLineGraph(lineGraph, t.getSource(), lg.second(), options);
+            ++result.exportedTrees;
         }
 
         result.exportedCommits = 1;
-        result.filterHits = new ExplainedFilterSummary(options.treeFilter());
 
         return new Pair<>(result, lineGraph.toString());
     }
@@ -94,34 +82,41 @@ public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, fina
     public static AnalysisResult toLineGraphFormat(final String repoName, final CommitDiff commitDiff, final StringBuilder lineGraph, final LineGraphExportOptions options) {
         final AnalysisResult result = new AnalysisResult(repoName);
 
-        final String hash = commitDiff.getCommitHash();
         for (final PatchDiff patchDiff : commitDiff.getPatchDiffs()) {
-            if (patchDiff.isValid()) {
-                //Logger.info("  Exporting DiffTree #{}", treeCounter);
-                final Pair<DiffTreeSerializeDebugData, String> patchDiffLg;
-                try {
-                    patchDiffLg = toLineGraphFormat(patchDiff.getDiffTree(), options);
-                } catch (Exception e) {
-                    options.onError().accept(patchDiff, e);
-                    break;
-                }
-
-                if (patchDiffLg != null) {
-                    result.debugData.append(patchDiffLg.first());
-                    composeTreeInLineGraph(lineGraph, patchDiff, patchDiffLg.second(), options);
-                    ++result.exportedTrees;
-                }
-            } else {
-                Logger.debug("  Skipping invalid patch for file {} at commit {}", patchDiff.getFileName(), hash);
+            try {
+                toLineGraphFormat(patchDiff, lineGraph, options, result);
+            } catch (Exception e) {
+                options.onError().accept(patchDiff, e);
+                break;
             }
         }
 
         result.exportedCommits = 1;
-        result.filterHits = new ExplainedFilterSummary(options.treeFilter());
 
         return result;
     }
-    
+
+    /**
+     * Writes the given patch in linegraph format to the given StringBuilder.
+     * @param patch The diff to convert to line graph format.
+     * @param lineGraph The string builder to write the result to.
+     * @param options Configuration options for the export, such as the format used for node and edge labels.
+     * @param result where the number of exported trees and debug data is updated
+     */
+    public static void toLineGraphFormat(final PatchDiff patch, final StringBuilder lineGraph, final LineGraphExportOptions options, AnalysisResult result) {
+        if (patch.isValid()) {
+            //Logger.info("  Exporting DiffTree #{}", treeCounter);
+            final Pair<DiffTreeSerializeDebugData, String> patchLg;
+            patchLg = toLineGraphFormat(patch.getDiffTree(), options);
+
+            result.debugData.append(patchLg.first());
+            composeTreeInLineGraph(lineGraph, patch, patchLg.second(), options);
+            ++result.exportedTrees;
+        } else {
+            Logger.debug("  Skipping invalid patch for file {} at commit {}", patch.getFileName(), patch.getCommitHash());
+        }
+    }
+
     /**
      * Produces the final linegraph file content.
      * Creates a linegraph header from the given DiffTreeSource using the {@link LineGraphExportOptions#treeFormat} in the given options.
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
index 57f3474bd..edba32dc7 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
@@ -3,7 +3,6 @@
 import org.tinylog.Logger;
 import org.variantsync.diffdetective.diff.PatchDiff;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
-import org.variantsync.diffdetective.diff.difftree.filter.ExplainedFilter;
 import org.variantsync.diffdetective.diff.difftree.render.DiffTreeRenderer;
 import org.variantsync.diffdetective.diff.difftree.render.PatchDiffRenderer;
 import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat;
@@ -12,8 +11,6 @@
 import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.function.BiConsumer;
 
 /**
@@ -34,15 +31,13 @@ public record LineGraphExportOptions(
 		DiffTreeLabelFormat treeFormat,
 		DiffNodeLabelFormat nodeFormat,
         EdgeLabelFormat edgeFormat,
-		ExplainedFilter<DiffTree> treeFilter,
-        List<DiffTreeTransformer> treePreProcessing,
         BiConsumer<PatchDiff, Exception> onError) {
 
     /**
      * Creates a export options with a neutral filter (that accepts all trees), no transformers, and that logs errors.
      */
     public LineGraphExportOptions(GraphFormat graphFormat, DiffTreeLabelFormat treeFormat, DiffNodeLabelFormat nodeFormat, EdgeLabelFormat edgeFormat) {
-        this(graphFormat, treeFormat, nodeFormat, edgeFormat, ExplainedFilter.Any(), new ArrayList<>(), LogError());
+        this(graphFormat, treeFormat, nodeFormat, edgeFormat, LogError());
     }
 
     /**
diff --git a/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java b/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java
index 83240bdfd..7b87a7935 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/DiffTreeMiner.java
@@ -80,17 +80,6 @@ public static LineGraphExportOptions MiningExportOptions(final Repository reposi
                 , new CommitDiffDiffTreeLabelFormat()
                 , nodeFormat
                 , EdgeFormat(nodeFormat)
-                , new ExplainedFilter<>(
-                        DiffTreeFilter.notEmpty(),
-                        DiffTreeFilter.moreThanOneArtifactNode(),
-                        /// We want to exclude patches that do not edit variability.
-                        /// In particular, we noticed that most edits just insert or delete artifacts (or replace it).
-                        /// This is reasonable and was also observed in previous studies: Edits to artifacts are more frequent than edits to variability.
-                        /// Yet, such edits cannot reveal compositions of more complex edits to variability.
-                        /// We thus filter them.
-                        DiffTreeFilter.hasAtLeastOneEditToVariability()
-                )
-                , Postprocessing(repository)
                 , LineGraphExportOptions.LogError()
                 .andThen(LineGraphExportOptions.RenderError())
                 .andThen(LineGraphExportOptions.SysExitOnError())
@@ -110,10 +99,20 @@ public static CommitHistoryAnalysisTaskFactory Mine() {
                 repo,
                 differ,
                 outputPath,
-                MiningExportOptions(repo),
+                new ExplainedFilter<>(
+                        DiffTreeFilter.notEmpty(),
+                        DiffTreeFilter.moreThanOneArtifactNode(),
+                        /// We want to exclude patches that do not edit variability.
+                        /// In particular, we noticed that most edits just insert or delete artifacts (or replace it).
+                        /// This is reasonable and was also observed in previous studies: Edits to artifacts are more frequent than edits to variability.
+                        /// Yet, such edits cannot reveal compositions of more complex edits to variability.
+                        /// We thus filter them.
+                        DiffTreeFilter.hasAtLeastOneEditToVariability()
+                ),
+                Postprocessing(repo),
                 MiningStrategy(),
                 commits
-        ));
+        ), MiningExportOptions(repo));
     }
 
     public static void main(String[] args) throws IOException {
diff --git a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
index f882faea7..d4739ee3d 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
@@ -20,14 +20,20 @@
 import java.util.List;
 
 public class MiningTask extends CommitHistoryAnalysisTask {
-    public MiningTask(final Options options) {
+    private final LineGraphExportOptions exportOptions;
+
+    public MiningTask(final Options options, final LineGraphExportOptions exportOptions) {
         super(options);
+
+        this.exportOptions = exportOptions;
     }
 
     @Override
     public AnalysisResult call() throws Exception {
         final AnalysisResult miningResult = super.call();
-        final LineGraphExportOptions exportOptions = options.exportOptions();
+        miningResult.putCustomInfo(MetadataKeys.TREEFORMAT, exportOptions.treeFormat().getName());
+        miningResult.putCustomInfo(MetadataKeys.NODEFORMAT, exportOptions.nodeFormat().getName());
+        miningResult.putCustomInfo(MetadataKeys.EDGEFORMAT, exportOptions.edgeFormat().getName());
 
         final Clock totalTime = new Clock();
 
@@ -48,14 +54,12 @@ public AnalysisResult call() throws Exception {
             }
 
             /*
-             * We export all difftrees that match our filter criteria (e.g., match more than one edit class).
-             * However, we count edit classes of all DiffTrees, even those that are not exported to Linegraph.
+             * We count the edit classes of all difftrees that match our filter criteria
+             * (e.g., match more than one edit class) and export them to the destination
+             * determined by the AnalysisStrategy.
              */
             final CommitDiff commitDiff = commitDiffResult.diff().get();
             final StringBuilder lineGraph = new StringBuilder();
-            miningResult.append(LineGraphExport.toLineGraphFormat(commitDiff, lineGraph, options.exportOptions()));
-            options.analysisStrategy().onCommit(commitDiff, lineGraph.toString());
-            options.exportOptions().treeFilter().resetExplanations();
 
             // Count edit classes
             int numDiffTrees = 0;
@@ -64,13 +68,15 @@ public AnalysisResult call() throws Exception {
 
                 if (patch.isValid()) {
                     final DiffTree t = patch.getDiffTree();
-                    DiffTreeTransformer.apply(exportOptions.treePreProcessing(), t);
+                    DiffTreeTransformer.apply(options.treePreProcessing(), t);
                     t.assertConsistency();
 
-                    if (!exportOptions.treeFilter().test(t)) {
+                    if (!options.treeFilter().test(t)) {
                         continue;
                     }
 
+                    LineGraphExport.toLineGraphFormat(patch, lineGraph, exportOptions, miningResult);
+
                     t.forAll(node -> {
                         if (node.isArtifact()) {
                             final EditClass editClass = ProposedEditClasses.Instance.match(node);
@@ -87,9 +93,13 @@ public AnalysisResult call() throws Exception {
 
                 patchStatistics.add(thisPatchesStatistics);
             }
+
+            options.analysisStrategy().onCommit(commitDiff, lineGraph.toString());
+
+            miningResult.exportedCommits += 1;
             miningResult.exportedTrees += numDiffTrees;
-            miningResult.filterHits.append(new ExplainedFilterSummary(exportOptions.treeFilter()));
-            exportOptions.treeFilter().resetExplanations();
+            miningResult.filterHits.append(new ExplainedFilterSummary(options.treeFilter()));
+            options.treeFilter().resetExplanations();
 
             // Only consider non-empty commits
             if (numDiffTrees > 0) {
diff --git a/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java b/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
index 4e71a46d5..c7db75300 100644
--- a/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
+++ b/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
@@ -9,7 +9,6 @@
 import org.variantsync.diffdetective.diff.CommitDiff;
 import org.variantsync.diffdetective.diff.PatchDiff;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExportOptions;
 import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.diff.result.CommitDiffResult;
 import org.variantsync.diffdetective.metadata.ExplainedFilterSummary;
@@ -33,7 +32,6 @@ public EditClassValidationTask(Options options) {
     public AnalysisResult call() throws Exception {
         // Setup. Obtain the result from the initial setup in the super class.
         final AnalysisResult miningResult = super.call();
-        final LineGraphExportOptions exportOptions = options.exportOptions();
         // List to store the process time of each commit.
         final List<CommitProcessTime> commitTimes = new ArrayList<>(HistoryAnalysis.COMMITS_TO_PROCESS_PER_THREAD_DEFAULT);
         // Clock for runtime measurement.
@@ -66,10 +64,10 @@ public AnalysisResult call() throws Exception {
                 for (final PatchDiff patch : commitDiff.getPatchDiffs()) {
                     if (patch.isValid()) {
                         final DiffTree t = patch.getDiffTree();
-                        DiffTreeTransformer.apply(exportOptions.treePreProcessing(), t);
+                        DiffTreeTransformer.apply(options.treePreProcessing(), t);
                         t.assertConsistency();
 
-                        if (!exportOptions.treeFilter().test(t)) {
+                        if (!options.treeFilter().test(t)) {
                             continue;
                         }
 
@@ -86,8 +84,8 @@ public AnalysisResult call() throws Exception {
                     }
                 }
                 miningResult.exportedTrees += numDiffTrees;
-                miningResult.filterHits.append(new ExplainedFilterSummary(exportOptions.treeFilter()));
-                exportOptions.treeFilter().resetExplanations();
+                miningResult.filterHits.append(new ExplainedFilterSummary(options.treeFilter()));
+                options.treeFilter().resetExplanations();
 
                 // Report the commit process time if the commit is not empty.
                 if (numDiffTrees > 0) {
diff --git a/src/main/java/org/variantsync/diffdetective/validation/Validation.java b/src/main/java/org/variantsync/diffdetective/validation/Validation.java
index 2f5cf2144..3e79bca12 100644
--- a/src/main/java/org/variantsync/diffdetective/validation/Validation.java
+++ b/src/main/java/org/variantsync/diffdetective/validation/Validation.java
@@ -56,7 +56,8 @@ public class Validation {
                     repo,
                     differ,
                     outputPath,
-                    ValidationExportOptions(repo),
+                    new ExplainedFilter<>(DiffTreeFilter.notEmpty()),
+                    List.of(new CutNonEditedSubtrees()),
                     new NullStrategy(),
                     commits
             ));
@@ -87,8 +88,6 @@ public static LineGraphExportOptions ValidationExportOptions(final Repository re
                 , new CommitDiffDiffTreeLabelFormat()
                 , nodeFormat
                 , EdgeFormat(nodeFormat)
-                , new ExplainedFilter<>(DiffTreeFilter.notEmpty())
-                , List.of(new CutNonEditedSubtrees())
                 , LineGraphExportOptions.LogError()
                 .andThen(LineGraphExportOptions.RenderError())
                 .andThen(LineGraphExportOptions.SysExitOnError())
diff --git a/src/test/java/MarlinDebug.java b/src/test/java/MarlinDebug.java
index 2673261bb..7ccd3f085 100644
--- a/src/test/java/MarlinDebug.java
+++ b/src/test/java/MarlinDebug.java
@@ -7,7 +7,6 @@
 import org.junit.Test;
 import org.prop4j.Node;
 import org.tinylog.Logger;
-import org.variantsync.diffdetective.analysis.CommitHistoryAnalysisTask;
 import org.variantsync.diffdetective.datasets.DatasetDescription;
 import org.variantsync.diffdetective.datasets.DatasetFactory;
 import org.variantsync.diffdetective.datasets.ParseOptions;
@@ -22,7 +21,6 @@
 import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.feature.CPPAnnotationParser;
 import org.variantsync.diffdetective.mining.DiffTreeMiner;
-import org.variantsync.diffdetective.mining.MiningTask;
 import org.variantsync.diffdetective.editclass.proposed.ProposedEditClasses;
 import org.variantsync.diffdetective.util.Clock;
 import org.variantsync.diffdetective.validation.Validation;
@@ -135,14 +133,12 @@ public static void asMiningTask(final RepoInspection repoInspection, final Strin
         final RevWalk revWalk = new RevWalk(git.getRepository());
         final RevCommit childCommit = revWalk.parseCommit(ObjectId.fromString(commitHash));
 
-        MiningTask m = new MiningTask(new CommitHistoryAnalysisTask.Options(
-                repoInspection.repo,
-                new GitDiffer(repoInspection.repo),
-                repoInspection.outputPath,
-                DiffTreeMiner.MiningExportOptions(repoInspection.repo),
-                DiffTreeMiner.MiningStrategy(),
-                List.of(childCommit)));
-        m.call();
+        DiffTreeMiner.Mine().create(
+            repoInspection.repo,
+            new GitDiffer(repoInspection.repo),
+            repoInspection.outputPath,
+            List.of(childCommit)
+        ).call();
     }
 
     public static void asValidationTask(final RepoInspection repoInspection, final String commitHash) throws Exception {

From 946295231a3bb2ae9d34c2fd463693a780f7bc71 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Sun, 25 Sep 2022 14:29:27 +0200
Subject: [PATCH 3/9] Use `OutputStream`s instead of `StringBuilder` in
 `LineGraphExport`

---
 .../analysis/strategies/AnalysisMonitor.java  |  6 +-
 .../analysis/strategies/AnalysisStrategy.java | 11 +++-
 .../strategies/AnalyzeAllThenExport.java      | 20 ++++--
 .../AnalyzeAndExportIncrementally.java        | 34 +++++-----
 .../strategies/CompositeAnalysisStrategy.java | 17 ++++-
 .../analysis/strategies/NullStrategy.java     |  6 +-
 .../difftree/render/DiffTreeRenderer.java     | 12 ++--
 .../difftree/serialize/LineGraphExport.java   | 65 +++++++++----------
 .../diffdetective/mining/MiningTask.java      | 53 +++++++--------
 .../postprocessing/MiningPostprocessing.java  | 12 ++--
 .../variantsync/diffdetective/util/IO.java    | 19 ++++++
 .../validation/EditClassValidationTask.java   |  2 +-
 src/test/java/LineGraphTest.java              | 43 ++++++------
 src/test/java/MarlinDebug.java                |  4 --
 src/test/java/TestMultiLineMacros.java        | 20 +++---
 15 files changed, 181 insertions(+), 143 deletions(-)

diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
index 860cd3817..3f52c374e 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisMonitor.java
@@ -4,6 +4,7 @@
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
 
+import java.io.OutputStream;
 import java.nio.file.Path;
 
 /**
@@ -29,8 +30,11 @@ public void start(Repository repo, Path outputPath) {
     }
 
     @Override
-    public void onCommit(CommitDiff commit, String lineGraph) {
+    public OutputStream onCommit(CommitDiff commit) {
+        // FIXME This function is called before processing the commit.
         monitor.addFinishedTasks(1);
+
+        return OutputStream.nullOutputStream();
     }
 
     @Override
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
index 8bec57262..adafa29bd 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalysisStrategy.java
@@ -3,6 +3,7 @@
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
 
+import java.io.OutputStream;
 import java.nio.file.Path;
 
 /**
@@ -27,11 +28,15 @@ public void start(Repository repo, Path outputPath) {
     }
 
     /**
-     * Invoked whenever the analysis processed a commit and converted it to linegraph format.
+     * Invoked before a commit is analyzed.
+     *
+     * The returned line graph export destination is closed after processing the commit given by
+     * {@code commit}.
+     *
      * @param commit The commit that was just processed.
-     * @param lineGraph The linegraph representation of the processed commit. Might be empty if no export to linegraph is desired.
+     * @return the line graph export destination
      */
-    public abstract void onCommit(CommitDiff commit, String lineGraph);
+    public abstract OutputStream onCommit(CommitDiff commit);
 
     /**
      * Invoked when the analysis is done for the current repository.
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
index eb6405abf..d2868057b 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
@@ -1,9 +1,14 @@
 package org.variantsync.diffdetective.analysis.strategies;
 
+import org.apache.commons.io.output.CloseShieldOutputStream;
+import org.tinylog.Logger;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
 import org.variantsync.diffdetective.util.IO;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.nio.file.Path;
 
 /**
@@ -11,23 +16,24 @@
  * @author Paul Bittner
  */
 public class AnalyzeAllThenExport extends AnalysisStrategy {
-    private StringBuilder waitForAll;
+    private ByteArrayOutputStream lineGraphDestination;
 
     @Override
     public void start(Repository repo, Path outputPath) {
         super.start(repo, outputPath);
-        waitForAll = new StringBuilder();
     }
 
     @Override
-    public void onCommit(CommitDiff commit, String lineGraph) {
-        waitForAll.append(lineGraph);
+    public OutputStream onCommit(CommitDiff commit) {
+        return new CloseShieldOutputStream(lineGraphDestination);
     }
 
     @Override
     public void end() {
-        final String lineGraph = waitForAll.toString();
-//        Logger.info("Writing file {}", outputPath);
-        IO.tryWrite(outputPath, lineGraph);
+        try (var finalDestination = IO.newBufferedOutputStream(outputPath)) {
+            lineGraphDestination.writeTo(finalDestination);
+        } catch (IOException e) {
+            Logger.error(e);
+        }
     }
 }
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
index 1ddd341ad..c9b04a8b6 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAndExportIncrementally.java
@@ -1,12 +1,16 @@
 package org.variantsync.diffdetective.analysis.strategies;
 
+import org.apache.commons.io.output.CloseShieldOutputStream;
 import org.tinylog.Logger;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
 import org.variantsync.diffdetective.util.IO;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 
 /**
  * Collects the linegraph representations generated by an analysis and exports them once a certain threshold of
@@ -21,7 +25,7 @@ public class AnalyzeAndExportIncrementally extends AnalysisStrategy {
     public static final int DEFAULT_NUMBER_OF_COMMITS_TO_EXPORT_AT_ONCE = 100;
     private final int commitsToExportAtOnce;
 
-    private StringBuilder nextChunkToExport;
+    private ByteArrayOutputStream lineGraphDestination;
     private int collectedCommits;
 
     /**
@@ -47,27 +51,27 @@ public void start(Repository repo, Path outputPath) {
         super.start(repo, outputPath);
 
         IO.tryDeleteFile(outputPath);
-        nextChunkToExport = new StringBuilder();
+
+        lineGraphDestination = new ByteArrayOutputStream();
         collectedCommits = 0;
     }
 
     @Override
-    public void onCommit(CommitDiff commit, String lineGraph) {
+    public OutputStream onCommit(CommitDiff commit) {
         ++collectedCommits;
-        nextChunkToExport.append(lineGraph);
-
-        if (collectedCommits >= commitsToExportAtOnce) {
-            exportAppend(outputPath, nextChunkToExport.toString());
-            nextChunkToExport = new StringBuilder();
+        if (collectedCommits > commitsToExportAtOnce) {
+            flush();
             collectedCommits = 0;
         }
+
+        // lineGraphDestination is reused for all commits. CloseShieldOutputStream ensures that it
+        // isn't closed after processing this commit.
+        return new CloseShieldOutputStream(lineGraphDestination);
     }
 
     @Override
     public void end() {
-        if (!nextChunkToExport.isEmpty()) {
-            exportAppend(outputPath, nextChunkToExport.toString());
-        }
+        flush();
     }
 
     /**
@@ -75,10 +79,10 @@ public void end() {
      * @param outputPath File to which the linegraph string should be appended.
      * @param linegraph String to append to the given file.
      */
-    public static void exportAppend(final Path outputPath, final String linegraph) {
-        try {
-//            Logger.info("Writing file {}", outputPath);
-            IO.append(outputPath, linegraph);
+    private void flush() {
+        try (var output = IO.newBufferedOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
+            lineGraphDestination.writeTo(output);
+            lineGraphDestination.reset();
         } catch (IOException exception) {
             Logger.error(exception);
         }
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
index cba412c49..3acdf2c72 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/CompositeAnalysisStrategy.java
@@ -1,8 +1,10 @@
 package org.variantsync.diffdetective.analysis.strategies;
 
+import org.apache.commons.io.output.TeeOutputStream;
 import org.variantsync.diffdetective.datasets.Repository;
 import org.variantsync.diffdetective.diff.CommitDiff;
 
+import java.io.OutputStream;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
@@ -33,10 +35,19 @@ public void start(Repository repo, Path outputPath) {
     }
 
     @Override
-    public void onCommit(CommitDiff commit, String lineGraph) {
-        for (final AnalysisStrategy s : strategies) {
-            s.onCommit(commit, lineGraph);
+    public OutputStream onCommit(CommitDiff commit) {
+        var it = strategies.iterator();
+
+        if (!it.hasNext()) {
+            return OutputStream.nullOutputStream();
         }
+
+        OutputStream destination = it.next().onCommit(commit);
+        while (it.hasNext()) {
+            destination = new TeeOutputStream(destination, it.next().onCommit(commit));
+        }
+
+        return destination;
     }
 
     @Override
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/NullStrategy.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/NullStrategy.java
index 79c7027bb..496a0cc74 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/NullStrategy.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/NullStrategy.java
@@ -1,5 +1,7 @@
 package org.variantsync.diffdetective.analysis.strategies;
 
+import java.io.OutputStream;
+
 import org.variantsync.diffdetective.diff.CommitDiff;
 
 /**
@@ -8,8 +10,8 @@
  */
 public class NullStrategy extends AnalysisStrategy {
     @Override
-    public void onCommit(CommitDiff commit, String lineGraph) {
-
+    public OutputStream onCommit(CommitDiff commit) {
+        return OutputStream.nullOutputStream();
     }
 
     @Override
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
index 4592c3880..203c32b3f 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
@@ -182,7 +182,7 @@ public boolean renderWithTreeFormat(final DiffTree tree, final String treeAndFil
      * The python script will produce an image file at the given directory.
      * @param tree The tree to render.
      * @param treeAndFileName A name for the written file as well as the tree (used as a caption in the produced image).
-     * @param directory The directory to which the the rendered file should be written to.
+     * @param directory The directory to which the rendered file should be written to.
      * @param options Configuration options for the rendering process.
      * @param exportOptions Configuration options for the intermediate export to the linegraph format.
      *                      Should be compatible with the render options.
@@ -194,12 +194,10 @@ public boolean renderWithTreeFormat(final DiffTree tree, final String treeAndFil
     private boolean render(final DiffTree tree, final String treeAndFileName, final Path directory, RenderOptions options, LineGraphExportOptions exportOptions, BiFunction<String, DiffTreeSource, String> treeHeader) {
         final Path tempFile = directory.resolve(treeAndFileName + ".lg");
 
-        final Pair<DiffTreeSerializeDebugData, String> result = LineGraphExport.toLineGraphFormat(tree, exportOptions);
-        Assert.assertNotNull(result);
-        final String lg = treeHeader.apply(treeAndFileName, tree.getSource()) + StringUtils.LINEBREAK + result.second();
-
-        try {
-            IO.write(tempFile, lg);
+        try (var destination = IO.newBufferedOutputStream(tempFile)) {
+            destination.write((treeHeader.apply(treeAndFileName, tree.getSource()) + StringUtils.LINEBREAK).getBytes());
+            final DiffTreeSerializeDebugData result = LineGraphExport.toLineGraphFormat(tree, exportOptions, destination);
+            Assert.assertNotNull(result);
         } catch (IOException e) {
             Logger.error(e, "Could not render difftree {} because", treeAndFileName);
             return false;
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
index a535b9d0e..e7ee874a7 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
@@ -1,6 +1,7 @@
 package org.variantsync.diffdetective.diff.difftree.serialize;
 
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 
 import org.tinylog.Logger;
 import org.variantsync.diffdetective.analysis.AnalysisResult;
@@ -9,7 +10,6 @@
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.DiffTreeSource;
 import org.variantsync.diffdetective.util.StringUtils;
-import org.variantsync.functjonal.Pair;
 
 /**
  * Class that contains functions for writing {@link CommitDiff}s and (sets of) {@link DiffTree}s to a linegraph file.
@@ -24,11 +24,10 @@ private LineGraphExport() {}
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @return A pair holding some debug information and the produced linegraph as a string.
      */
-    public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final DiffTree diffTree, final LineGraphExportOptions options) {
+    public static DiffTreeSerializeDebugData toLineGraphFormat(final DiffTree diffTree, final LineGraphExportOptions options, OutputStream destination) throws IOException {
         final var exporter = new LineGraphExporter(options);
-        var output = new ByteArrayOutputStream();
-        exporter.exportDiffTree(diffTree, output);
-        return new Pair<>(exporter.getDebugData(), output.toString());
+        exporter.exportDiffTree(diffTree, destination);
+        return exporter.getDebugData();
     }
 
     /**
@@ -38,37 +37,35 @@ public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final D
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @return A pair of (1) metadata about the exported DiffTrees, and (2) the produced linegraph as String.
      */
-    public static Pair<AnalysisResult, String> toLineGraphFormat(final String repoName, final Iterable<DiffTree> trees, final LineGraphExportOptions options) {
+    public static AnalysisResult toLineGraphFormat(final String repoName, final Iterable<DiffTree> trees, final LineGraphExportOptions options, OutputStream destination) throws IOException {
         final AnalysisResult result = new AnalysisResult(repoName);
 
-        final StringBuilder lineGraph = new StringBuilder();
         for (final DiffTree t : trees) {
-            final Pair<DiffTreeSerializeDebugData, String> lg = toLineGraphFormat(t, options);
-
-            result.debugData.append(lg.first());
-            composeTreeInLineGraph(lineGraph, t.getSource(), lg.second(), options);
+            destination.write(lineGraphHeader(t.getSource(), options).getBytes());
+            result.debugData.append(toLineGraphFormat(t, options, destination));
+            destination.write(lineGraphFooter().getBytes());
             ++result.exportedTrees;
         }
 
         result.exportedCommits = 1;
 
-        return new Pair<>(result, lineGraph.toString());
+        return result;
     }
 
     /**
-     * Same as {@link LineGraphExport#toLineGraphFormat(String, Iterable, LineGraphExportOptions)} but with an
+     * Same as {@link LineGraphExport#toLineGraphFormat(String, Iterable, LineGraphExportOptions, OutputStream)} but with an
      * {@link AnalysisResult#NO_REPO unkown repository}.
      */
-    public static Pair<AnalysisResult, String> toLineGraphFormat(final Iterable<DiffTree> trees, final LineGraphExportOptions options) {
-        return toLineGraphFormat(AnalysisResult.NO_REPO, trees, options);
+    public static AnalysisResult toLineGraphFormat(final Iterable<DiffTree> trees, final LineGraphExportOptions options, OutputStream destination) throws IOException {
+        return toLineGraphFormat(AnalysisResult.NO_REPO, trees, options, destination);
     }
 
     /**
-     * Same as {@link LineGraphExport#toLineGraphFormat(String, CommitDiff, StringBuilder, LineGraphExportOptions)}
+     * Same as {@link LineGraphExport#toLineGraphFormat(String, CommitDiff, LineGraphExportOptions, OutputStream)}
      * but with an {@link AnalysisResult#NO_REPO unkown repository}.
      */
-    public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, final StringBuilder lineGraph, final LineGraphExportOptions options) {
-        return toLineGraphFormat(AnalysisResult.NO_REPO, commitDiff, lineGraph, options);
+    public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, final LineGraphExportOptions options, OutputStream destination) throws IOException {
+        return toLineGraphFormat(AnalysisResult.NO_REPO, commitDiff, options, destination);
     }
 
     /**
@@ -79,12 +76,12 @@ public static AnalysisResult toLineGraphFormat(final CommitDiff commitDiff, fina
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @return The number of the next diff tree to export (updated value of treeCounter).
      */
-    public static AnalysisResult toLineGraphFormat(final String repoName, final CommitDiff commitDiff, final StringBuilder lineGraph, final LineGraphExportOptions options) {
+    public static AnalysisResult toLineGraphFormat(final String repoName, final CommitDiff commitDiff, LineGraphExportOptions options, OutputStream destination) throws IOException {
         final AnalysisResult result = new AnalysisResult(repoName);
 
         for (final PatchDiff patchDiff : commitDiff.getPatchDiffs()) {
             try {
-                toLineGraphFormat(patchDiff, lineGraph, options, result);
+                toLineGraphFormat(patchDiff, options, destination, result);
             } catch (Exception e) {
                 options.onError().accept(patchDiff, e);
                 break;
@@ -103,14 +100,15 @@ public static AnalysisResult toLineGraphFormat(final String repoName, final Comm
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @param result where the number of exported trees and debug data is updated
      */
-    public static void toLineGraphFormat(final PatchDiff patch, final StringBuilder lineGraph, final LineGraphExportOptions options, AnalysisResult result) {
+    public static void toLineGraphFormat(final PatchDiff patch, final LineGraphExportOptions options, OutputStream destination, AnalysisResult result) throws IOException {
         if (patch.isValid()) {
             //Logger.info("  Exporting DiffTree #{}", treeCounter);
-            final Pair<DiffTreeSerializeDebugData, String> patchLg;
-            patchLg = toLineGraphFormat(patch.getDiffTree(), options);
 
-            result.debugData.append(patchLg.first());
-            composeTreeInLineGraph(lineGraph, patch, patchLg.second(), options);
+            //Logger.info("  Exporting DiffTree #{}", treeCounter);
+            destination.write(lineGraphHeader(patch, options).getBytes());
+            result.debugData.append(toLineGraphFormat(patch.getDiffTree(), options, destination));
+            destination.write(lineGraphFooter().getBytes());
+
             ++result.exportedTrees;
         } else {
             Logger.debug("  Skipping invalid patch for file {} at commit {}", patch.getFileName(), patch.getCommitHash());
@@ -123,15 +121,14 @@ public static void toLineGraphFormat(final PatchDiff patch, final StringBuilder
      * Then appends the already created file content for nodes and edges.
      * @param lineGraph The string builder to write the result to.
      * @param source The {@link DiffTreeSource} that describes where the DiffTree whose content is written to the file originated from.
-     * @param nodesAndEdges Result from {@link #toLineGraphFormat(DiffTree, LineGraphExportOptions)}. Holds all nodes and edges in linegraph format, separated by a newline each.
+     * @param nodesAndEdges Result from {@link #toLineGraphFormat(DiffTree, LineGraphExportOptions, OutputStream)}. Holds all nodes and edges in linegraph format, separated by a newline each.
      * @param options {@link LineGraphExportOptions} used to determine the treeFormat for the header.
      */
-    public static void composeTreeInLineGraph(final StringBuilder lineGraph, final DiffTreeSource source, final String nodesAndEdges, final LineGraphExportOptions options) {
-    	lineGraph
-    		.append(options.treeFormat().toLineGraphLine(source)) // print "t # $LABEL"
-    		.append(StringUtils.LINEBREAK)
-    		.append(nodesAndEdges)
-    		.append(StringUtils.LINEBREAK)
-    		.append(StringUtils.LINEBREAK);
+    private static String lineGraphHeader(final DiffTreeSource source, final LineGraphExportOptions options) {
+        return options.treeFormat().toLineGraphLine(source) + StringUtils.LINEBREAK;
+    }
+
+    private static String lineGraphFooter() {
+        return StringUtils.LINEBREAK + StringUtils.LINEBREAK;
     }
 }
diff --git a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
index d4739ee3d..3fb6e9b28 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
@@ -58,44 +58,41 @@ public AnalysisResult call() throws Exception {
              * (e.g., match more than one edit class) and export them to the destination
              * determined by the AnalysisStrategy.
              */
+            int numDiffTrees = 0;
             final CommitDiff commitDiff = commitDiffResult.diff().get();
-            final StringBuilder lineGraph = new StringBuilder();
+            try (var lineGraphDestination = options.analysisStrategy().onCommit(commitDiff)) {
+                for (final PatchDiff patch : commitDiff.getPatchDiffs()) {
+                    final PatchStatistics thisPatchesStatistics = new PatchStatistics(patch, ProposedEditClasses.Instance);
 
-            // Count edit classes
-            int numDiffTrees = 0;
-            for (final PatchDiff patch : commitDiff.getPatchDiffs()) {
-                final PatchStatistics thisPatchesStatistics = new PatchStatistics(patch, ProposedEditClasses.Instance);
+                    if (patch.isValid()) {
+                        final DiffTree t = patch.getDiffTree();
+                        DiffTreeTransformer.apply(options.treePreProcessing(), t);
+                        t.assertConsistency();
 
-                if (patch.isValid()) {
-                    final DiffTree t = patch.getDiffTree();
-                    DiffTreeTransformer.apply(options.treePreProcessing(), t);
-                    t.assertConsistency();
+                        if (!options.treeFilter().test(t)) {
+                            continue;
+                        }
 
-                    if (!options.treeFilter().test(t)) {
-                        continue;
-                    }
+                        LineGraphExport.toLineGraphFormat(patch, exportOptions, lineGraphDestination, miningResult);
 
-                    LineGraphExport.toLineGraphFormat(patch, lineGraph, exportOptions, miningResult);
+                        t.forAll(node -> {
+                            if (node.isArtifact()) {
+                                final EditClass editClass = ProposedEditClasses.Instance.match(node);
+                                miningResult.editClassCounts.reportOccurrenceFor(
+                                        editClass,
+                                        commitDiff
+                                );
+                                thisPatchesStatistics.editClassCount().increment(editClass);
+                            }
+                        });
 
-                    t.forAll(node -> {
-                        if (node.isArtifact()) {
-                            final EditClass editClass = ProposedEditClasses.Instance.match(node);
-                            miningResult.editClassCounts.reportOccurrenceFor(
-                                    editClass,
-                                    commitDiff
-                            );
-                            thisPatchesStatistics.editClassCount().increment(editClass);
-                        }
-                    });
+                        ++numDiffTrees;
+                    }
 
-                    ++numDiffTrees;
+                    patchStatistics.add(thisPatchesStatistics);
                 }
-
-                patchStatistics.add(thisPatchesStatistics);
             }
 
-            options.analysisStrategy().onCommit(commitDiff, lineGraph.toString());
-
             miningResult.exportedCommits += 1;
             miningResult.exportedTrees += numDiffTrees;
             miningResult.filterHits.append(new ExplainedFilterSummary(options.treeFilter()));
diff --git a/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java b/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java
index 1ad3c512e..ee3a7e646 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/postprocessing/MiningPostprocessing.java
@@ -1,6 +1,6 @@
 package org.variantsync.diffdetective.mining.postprocessing;
 
-import org.variantsync.diffdetective.analysis.AnalysisResult;
+import org.tinylog.Logger;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.render.DiffTreeRenderer;
 import org.variantsync.diffdetective.diff.difftree.render.RenderOptions;
@@ -9,7 +9,6 @@
 import org.variantsync.diffdetective.mining.DiffTreeMiner;
 import org.variantsync.diffdetective.util.FileUtils;
 import org.variantsync.diffdetective.util.IO;
-import org.variantsync.functjonal.Pair;
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -137,8 +136,13 @@ public static void postprocessAndInterpretResults(
                 ++patternNo;
             }
         } else {
-            final Pair<AnalysisResult, String> lineGraph = LineGraphExport.toLineGraphFormat(semanticPatterns, EXPORT_OPTIONS);
-            IO.tryWrite(outputDir.resolve("candidates.lg"), lineGraph.second());
+            Path destinationPath = outputDir.resolve("candidates.lg");
+
+            try (var destination = IO.newBufferedOutputStream(destinationPath)) {
+                LineGraphExport.toLineGraphFormat(semanticPatterns, EXPORT_OPTIONS, destination);
+            } catch (IOException e) {
+                Logger.error(e);
+            }
         }
     }
 }
diff --git a/src/main/java/org/variantsync/diffdetective/util/IO.java b/src/main/java/org/variantsync/diffdetective/util/IO.java
index ff0f0ff84..a99509889 100644
--- a/src/main/java/org/variantsync/diffdetective/util/IO.java
+++ b/src/main/java/org/variantsync/diffdetective/util/IO.java
@@ -7,6 +7,7 @@
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import java.util.Optional;
@@ -58,6 +59,24 @@ public static void exportCsv(String fileName, String[] headers, Object[]... obje
         writer.close();
     }
 
+    /**
+     * Same as {@link Files#newOutputStream} but creates all parent directories of
+     * {@code file} and wraps the result in a {@link BuferedOutputStream}.
+     */
+    public static BufferedOutputStream newBufferedOutputStream(Path file, OpenOption... openOptions) throws IOException {
+        if (file.getParent() != null) {
+            Files.createDirectories(file.getParent());
+        }
+
+        var outputStream = Files.newOutputStream(file, openOptions);
+        try {
+            return new BufferedOutputStream(outputStream);
+        } catch (Exception e) {
+            outputStream.close();
+            throw e;
+        }
+    }
+
     /**
      * Writes the given text to the given file.
      * Creates a new file and its parent directories if necessary. It assumes that no file exists
diff --git a/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java b/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
index c7db75300..abf91c626 100644
--- a/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
+++ b/src/main/java/org/variantsync/diffdetective/validation/EditClassValidationTask.java
@@ -57,7 +57,7 @@ public AnalysisResult call() throws Exception {
 
                 // extract the produced commit diff and inform the strategy
                 final CommitDiff commitDiff = commitDiffResult.diff().get();
-                options.analysisStrategy().onCommit(commitDiff, "");
+                options.analysisStrategy().onCommit(commitDiff).close();
 
                 // Count edit class matches
                 int numDiffTrees = 0;
diff --git a/src/test/java/LineGraphTest.java b/src/test/java/LineGraphTest.java
index 5db6ddc2d..6ee2d7cbb 100644
--- a/src/test/java/LineGraphTest.java
+++ b/src/test/java/LineGraphTest.java
@@ -1,24 +1,22 @@
+import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.tinylog.Logger;
-import org.variantsync.diffdetective.diff.difftree.CommitDiffDiffTreeSource;
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.serialize.*;
 import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.DefaultEdgeLabelFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.nodeformat.LabelOnlyDiffNodeFormat;
 import org.variantsync.diffdetective.diff.difftree.serialize.treeformat.CommitDiffDiffTreeLabelFormat;
-import org.variantsync.diffdetective.util.FileUtils;
+import org.variantsync.diffdetective.util.IO;
 
 import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.StringReader;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
-import java.util.Objects;
 
-import static org.junit.Assert.assertEquals;
 /**
  * For testing the import of a line graph.
  */
@@ -52,8 +50,23 @@ public void idempotentReadWrite() throws IOException {
                 diffTrees = LineGraphImport.fromLineGraph(lineGraph, testFile, IMPORT_OPTIONS);
             }
             assertConsistencyForAll(diffTrees);
-            final String lineGraphResult = exportDiffTreeToLineGraph(diffTrees);
-            TestUtils.assertEqualToFile(testFile, lineGraphResult);
+
+            Path actualPath = testFile.getParent().resolve(testFile.getFileName().toString() + ".actual");
+            try (var output = IO.newBufferedOutputStream(actualPath)) {
+                LineGraphExport.toLineGraphFormat(diffTrees, EXPORT_OPTIONS, output);
+            }
+
+            try (
+                    var expectedFile = Files.newBufferedReader(testFile);
+                    var actualFile = Files.newBufferedReader(actualPath);
+            ) {
+                if (!IOUtils.contentEqualsIgnoreEOL(expectedFile, actualFile)) {
+                    Assert.fail("The file " + testFile + " couldn't be exported or imported without modifications");
+                } else {
+                    // Only keep output file on errors
+                    Files.delete(actualPath);
+                }
+            }
         }
 	}
 	
@@ -68,20 +81,4 @@ private static void assertConsistencyForAll(final List<DiffTree> treeList) {
 //        }
 		treeList.forEach(DiffTree::assertConsistency);
 	}
-	
-	/**
-	 * Exports computed trees to line graph.
-	 * 
-	 * @param treeList A list of {@link DiffTree DiffTrees}
-	 * @return The computed line graph
-	 */
-	private static String exportDiffTreeToLineGraph(final List<DiffTree> treeList) {
-        final StringBuilder lineGraphOutput = new StringBuilder();
-        for (var tree : treeList) {
-        	if (tree.getSource() instanceof CommitDiffDiffTreeSource source) {
-                LineGraphExport.composeTreeInLineGraph(lineGraphOutput, source, Objects.requireNonNull(LineGraphExport.toLineGraphFormat(tree, EXPORT_OPTIONS)).second(), EXPORT_OPTIONS);
-        	} else throw new RuntimeException("The DiffTreeSoruce of DiffTree " + tree + " is not a CommitDiffDiffTreeSource: " + tree.getSource());
-        }
-        return lineGraphOutput.toString();
-	}
 }
diff --git a/src/test/java/MarlinDebug.java b/src/test/java/MarlinDebug.java
index 7ccd3f085..3c99fa735 100644
--- a/src/test/java/MarlinDebug.java
+++ b/src/test/java/MarlinDebug.java
@@ -17,7 +17,6 @@
 import org.variantsync.diffdetective.diff.difftree.DiffTree;
 import org.variantsync.diffdetective.diff.difftree.parse.DiffTreeParser;
 import org.variantsync.diffdetective.diff.difftree.parse.IllFormedAnnotationException;
-import org.variantsync.diffdetective.diff.difftree.serialize.LineGraphExport;
 import org.variantsync.diffdetective.diff.difftree.transform.DiffTreeTransformer;
 import org.variantsync.diffdetective.feature.CPPAnnotationParser;
 import org.variantsync.diffdetective.mining.DiffTreeMiner;
@@ -122,9 +121,6 @@ public static void testCommit(final RepoInspection repoInspection, final String
                 Logger.info("  End processing {}", patch);
             }
         }
-
-        StringBuilder bigB = new StringBuilder();
-        LineGraphExport.toLineGraphFormat(commitDiff, bigB, DiffTreeMiner.MiningExportOptions(repoInspection.repo));
     }
 
     public static void asMiningTask(final RepoInspection repoInspection, final String commitHash) throws Exception {
diff --git a/src/test/java/TestMultiLineMacros.java b/src/test/java/TestMultiLineMacros.java
index e2ab633b7..b8941829a 100644
--- a/src/test/java/TestMultiLineMacros.java
+++ b/src/test/java/TestMultiLineMacros.java
@@ -13,7 +13,6 @@
 import org.variantsync.diffdetective.diff.difftree.serialize.treeformat.CommitDiffDiffTreeLabelFormat;
 import org.variantsync.diffdetective.util.IO;
 import org.variantsync.diffdetective.util.StringUtils;
-import org.variantsync.functjonal.Pair;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -33,18 +32,17 @@ public void diffToDiffTree(LineGraphExportOptions exportOptions, Path p) throws
                     DiffNodeParser.Default).unwrap().getSuccess();
         }
 
-        final Pair<DiffTreeSerializeDebugData, String> result = LineGraphExport.toLineGraphFormat(tree, exportOptions);
-        Assert.assertNotNull(result);
-        final DiffTreeSerializeDebugData debugData = result.first();
-        Logger.info("Parsed {} nodes of diff type NON.", debugData.numExportedNonNodes);
-        Logger.info("Parsed {} nodes of diff type ADD.", debugData.numExportedAddNodes);
-        Logger.info("Parsed {} nodes of diff type REM.", debugData.numExportedRemNodes);
+        try (var destination = IO.newBufferedOutputStream(resDir.resolve("gen").resolve(p.getFileName() + ".lg"))) {
+            destination.write(("t # 1" + StringUtils.LINEBREAK).getBytes());
 
-        final String lg = "t # 1" +
-                StringUtils.LINEBREAK +
-                result.second();
 
-        IO.write(resDir.resolve("gen").resolve(p.getFileName() + ".lg"), lg);
+            final DiffTreeSerializeDebugData debugData = LineGraphExport.toLineGraphFormat(tree, exportOptions, destination);
+            Assert.assertNotNull(debugData);
+            Logger.info("Parsed {} nodes of diff type NON.", debugData.numExportedNonNodes);
+            Logger.info("Parsed {} nodes of diff type ADD.", debugData.numExportedAddNodes);
+            Logger.info("Parsed {} nodes of diff type REM.", debugData.numExportedRemNodes);
+
+        }
     }
 
     @Test

From c29a9e493238d924d2cefe9ada9fb98d60615e33 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Thu, 29 Sep 2022 19:52:28 +0200
Subject: [PATCH 4/9] Factor out directory creation to a utility function

---
 .../difftree/render/DiffTreeRenderer.java     |  1 -
 .../variantsync/diffdetective/util/IO.java    | 21 +++++++++++--------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
index 203c32b3f..7dc8011be 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/render/DiffTreeRenderer.java
@@ -15,7 +15,6 @@
 import org.variantsync.diffdetective.util.Assert;
 import org.variantsync.diffdetective.util.IO;
 import org.variantsync.diffdetective.util.StringUtils;
-import org.variantsync.functjonal.Pair;
 
 import java.io.IOException;
 import java.nio.file.Files;
diff --git a/src/main/java/org/variantsync/diffdetective/util/IO.java b/src/main/java/org/variantsync/diffdetective/util/IO.java
index a99509889..a6485711c 100644
--- a/src/main/java/org/variantsync/diffdetective/util/IO.java
+++ b/src/main/java/org/variantsync/diffdetective/util/IO.java
@@ -60,13 +60,20 @@ public static void exportCsv(String fileName, String[] headers, Object[]... obje
     }
 
     /**
-     * Same as {@link Files#newOutputStream} but creates all parent directories of
-     * {@code file} and wraps the result in a {@link BuferedOutputStream}.
+     * Creates all parent directories of {@code file}.
      */
-    public static BufferedOutputStream newBufferedOutputStream(Path file, OpenOption... openOptions) throws IOException {
+    public static void createParentDirectories(Path file) throws IOException {
         if (file.getParent() != null) {
             Files.createDirectories(file.getParent());
         }
+    }
+
+    /**
+     * Same as {@link Files#newOutputStream} but creates all parent directories of
+     * {@code file} and wraps the result in a {@link BuferedOutputStream}.
+     */
+    public static BufferedOutputStream newBufferedOutputStream(Path file, OpenOption... openOptions) throws IOException {
+        createParentDirectories(file);
 
         var outputStream = Files.newOutputStream(file, openOptions);
         try {
@@ -88,9 +95,7 @@ public static BufferedOutputStream newBufferedOutputStream(Path file, OpenOption
      * the file, or the text cannot be encoded using UTF-8
      */
     public static void write(final Path p, final String text) throws IOException {
-        if (p.getParent() != null) {
-            Files.createDirectories(p.getParent());
-        }
+        createParentDirectories(p);
         Files.writeString(p, text, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
     }
 
@@ -105,9 +110,7 @@ public static void write(final Path p, final String text) throws IOException {
      * cannot be encoded using UTF-8
      */
     public static void append(final Path p, final String text) throws IOException {
-        if (p.getParent() != null) {
-            Files.createDirectories(p.getParent());
-        }
+        createParentDirectories(p);
         Files.writeString(p, text, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
     }
 

From ebe773898a022bf773790b12ac33fe9a6a5c1775 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Thu, 29 Sep 2022 19:58:16 +0200
Subject: [PATCH 5/9] Reuse `AnalyzeAndExportIncrementally` for
 `AnalyzeAllThenExport`

---
 .../strategies/AnalyzeAllThenExport.java      | 34 ++-----------------
 1 file changed, 3 insertions(+), 31 deletions(-)

diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
index d2868057b..369ac00d1 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllThenExport.java
@@ -1,39 +1,11 @@
 package org.variantsync.diffdetective.analysis.strategies;
 
-import org.apache.commons.io.output.CloseShieldOutputStream;
-import org.tinylog.Logger;
-import org.variantsync.diffdetective.datasets.Repository;
-import org.variantsync.diffdetective.diff.CommitDiff;
-import org.variantsync.diffdetective.util.IO;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Path;
-
 /**
  * Collects all linegraph representations generated by an analysis and exports them at the end.
  * @author Paul Bittner
  */
-public class AnalyzeAllThenExport extends AnalysisStrategy {
-    private ByteArrayOutputStream lineGraphDestination;
-
-    @Override
-    public void start(Repository repo, Path outputPath) {
-        super.start(repo, outputPath);
-    }
-
-    @Override
-    public OutputStream onCommit(CommitDiff commit) {
-        return new CloseShieldOutputStream(lineGraphDestination);
-    }
-
-    @Override
-    public void end() {
-        try (var finalDestination = IO.newBufferedOutputStream(outputPath)) {
-            lineGraphDestination.writeTo(finalDestination);
-        } catch (IOException e) {
-            Logger.error(e);
-        }
+public class AnalyzeAllThenExport extends AnalyzeAndExportIncrementally {
+    public AnalyzeAllThenExport() {
+        super(Integer.MAX_VALUE);
     }
 }

From 4eebf7974de4cb9924559c0784b290e6bc084776 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Sun, 9 Oct 2022 09:59:40 +0200
Subject: [PATCH 6/9] Signal failure when exiting because of an error

---
 .../diffdetective/analysis/CommitHistoryAnalysisTask.java     | 4 ++--
 .../variantsync/diffdetective/analysis/HistoryAnalysis.java   | 2 +-
 .../diff/difftree/serialize/LineGraphExportOptions.java       | 2 +-
 .../java/org/variantsync/diffdetective/metadata/Metadata.java | 2 +-
 .../diffdetective/tablegen/MiningResultAccumulator.java       | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java b/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
index 7bc86c676..8df76b339 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/CommitHistoryAnalysisTask.java
@@ -86,7 +86,7 @@ public static void exportCommitTimes(final List<CommitProcessTime> commitTimes,
             IO.write(pathToOutputFile, times.toString());
         } catch (IOException e) {
             Logger.error(e);
-            System.exit(0);
+            System.exit(1);
         }
     }
 
@@ -102,7 +102,7 @@ public static void exportPatchStatistics(final List<PatchStatistics> commitTimes
             IO.write(pathToOutputFile, csv);
         } catch (IOException e) {
             Logger.error(e);
-            System.exit(0);
+            System.exit(1);
         }
     }
 }
diff --git a/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java b/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
index 92b7018c5..db844520b 100644
--- a/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
+++ b/src/main/java/org/variantsync/diffdetective/analysis/HistoryAnalysis.java
@@ -150,7 +150,7 @@ public static void analyzeAsync(
             }
         } catch (Exception e) {
             Logger.error(e, "Failed to run all mining task");
-            System.exit(0);
+            System.exit(1);
         }
 
         final double runtime = clock.getPassedSeconds();
diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
index edba32dc7..3af617805 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExportOptions.java
@@ -77,6 +77,6 @@ public static BiConsumer<PatchDiff, Exception> RenderError() {
      * Default value for {@link #onError} that exits the program immediately upon an error with {@link System#exit(int)}.
      */
     public static BiConsumer<PatchDiff, Exception> SysExitOnError() {
-        return (p, e) -> System.exit(0);
+        return (p, e) -> System.exit(1);
     }
 }
diff --git a/src/main/java/org/variantsync/diffdetective/metadata/Metadata.java b/src/main/java/org/variantsync/diffdetective/metadata/Metadata.java
index f467dfe1a..5eb7cb199 100644
--- a/src/main/java/org/variantsync/diffdetective/metadata/Metadata.java
+++ b/src/main/java/org/variantsync/diffdetective/metadata/Metadata.java
@@ -75,7 +75,7 @@ default String exportTo(final Path file) {
             return result;
         } catch (IOException e) {
             Logger.error(e);
-            System.exit(0);
+            System.exit(1);
             return "";
         }
     }
diff --git a/src/main/java/org/variantsync/diffdetective/tablegen/MiningResultAccumulator.java b/src/main/java/org/variantsync/diffdetective/tablegen/MiningResultAccumulator.java
index 1012d9e80..015b3c6e7 100644
--- a/src/main/java/org/variantsync/diffdetective/tablegen/MiningResultAccumulator.java
+++ b/src/main/java/org/variantsync/diffdetective/tablegen/MiningResultAccumulator.java
@@ -143,7 +143,7 @@ public static void main(final String[] args) throws IOException, ParseException
                          automationResult = FindMedianCommitTime.getResultOfDirectory(automationResultDir);
                     } catch (IOException e) {
                         Logger.error("Could not load automation results for dataset {} in {}", dataset.name(), automationResultDir);
-                        System.exit(0);
+                        System.exit(1);
                         return null;
                     }
 

From 1761bce0b05feaf700cfee7b9691bda3a6ad3e21 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Fri, 30 Sep 2022 00:30:01 +0200
Subject: [PATCH 7/9] Create an export strategy using the default Java
 buffering

---
 .../strategies/AnalyzeAllAndExport.java       | 48 +++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllAndExport.java

diff --git a/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllAndExport.java b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllAndExport.java
new file mode 100644
index 000000000..c0c5df437
--- /dev/null
+++ b/src/main/java/org/variantsync/diffdetective/analysis/strategies/AnalyzeAllAndExport.java
@@ -0,0 +1,48 @@
+package org.variantsync.diffdetective.analysis.strategies;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Path;
+
+import org.apache.commons.io.output.CloseShieldOutputStream;
+import org.tinylog.Logger;
+import org.variantsync.diffdetective.datasets.Repository;
+import org.variantsync.diffdetective.diff.CommitDiff;
+import org.variantsync.diffdetective.util.IO;
+
+/**
+ * Exports all linegraph representations generated by an analysis directly to a file.
+ * The file operations are buffered by Java, so it flushes them when it thinks there are enough of
+ * them. In contrast to `AnalyzeAndExportIncrementally` this relies on the sanity of the Java
+ * defaults
+ *
+ * @author Benjamin Moosherr
+ */
+public class AnalyzeAllAndExport extends AnalysisStrategy {
+    private OutputStream lineGraphDestination;
+
+    @Override
+    public void start(Repository repo, Path outputPath) {
+        try {
+            lineGraphDestination = IO.newBufferedOutputStream(outputPath);
+        } catch (IOException e) {
+            Logger.error(e);
+        }
+    }
+
+    @Override
+    public OutputStream onCommit(CommitDiff commit) {
+        // lineGraphDestination is reused for all commits. CloseShieldOutputStream ensures that it
+        // isn't closed after processing this commit.
+        return new CloseShieldOutputStream(lineGraphDestination);
+    }
+
+    @Override
+    public void end() {
+        try {
+            lineGraphDestination.close();
+        } catch (IOException e) {
+            Logger.error(e);
+        }
+    }
+}

From d29f9e423a28c1347fd995fd60d2997510024ef2 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Mon, 17 Oct 2022 11:43:40 +0200
Subject: [PATCH 8/9] Deprecate the MarlinDebug test

This test needs manual inspection of the results.
---
 src/test/java/MarlinDebug.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/test/java/MarlinDebug.java b/src/test/java/MarlinDebug.java
index 3c99fa735..4936e4619 100644
--- a/src/test/java/MarlinDebug.java
+++ b/src/test/java/MarlinDebug.java
@@ -29,6 +29,7 @@
 import java.nio.file.Paths;
 import java.util.List;
 
+@Deprecated
 public class MarlinDebug {
     private record RepoInspection(
             List<String> suspiciousCommits,

From 1a980165e13d6e009342e56af7e99876da950963 Mon Sep 17 00:00:00 2001
From: Benjamin Moosherr <benjamin.moosherr@uni-ulm.de>
Date: Mon, 17 Oct 2022 11:45:58 +0200
Subject: [PATCH 9/9] Compose AnalysisResult instead of mutating it as
 parameter

---
 .../diff/difftree/serialize/LineGraphExport.java          | 8 ++++++--
 .../org/variantsync/diffdetective/mining/MiningTask.java  | 2 +-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
index e7ee874a7..38a8cc7cb 100644
--- a/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
+++ b/src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java
@@ -81,7 +81,7 @@ public static AnalysisResult toLineGraphFormat(final String repoName, final Comm
 
         for (final PatchDiff patchDiff : commitDiff.getPatchDiffs()) {
             try {
-                toLineGraphFormat(patchDiff, options, destination, result);
+                result.append(toLineGraphFormat(repoName, patchDiff, options, destination));
             } catch (Exception e) {
                 options.onError().accept(patchDiff, e);
                 break;
@@ -100,7 +100,9 @@ public static AnalysisResult toLineGraphFormat(final String repoName, final Comm
      * @param options Configuration options for the export, such as the format used for node and edge labels.
      * @param result where the number of exported trees and debug data is updated
      */
-    public static void toLineGraphFormat(final PatchDiff patch, final LineGraphExportOptions options, OutputStream destination, AnalysisResult result) throws IOException {
+    public static AnalysisResult toLineGraphFormat(final String repoName, final PatchDiff patch, final LineGraphExportOptions options, OutputStream destination) throws IOException {
+        final AnalysisResult result = new AnalysisResult(repoName);
+
         if (patch.isValid()) {
             //Logger.info("  Exporting DiffTree #{}", treeCounter);
 
@@ -113,6 +115,8 @@ public static void toLineGraphFormat(final PatchDiff patch, final LineGraphExpor
         } else {
             Logger.debug("  Skipping invalid patch for file {} at commit {}", patch.getFileName(), patch.getCommitHash());
         }
+
+        return result;
     }
 
     /**
diff --git a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
index 3fb6e9b28..81e48c3ba 100644
--- a/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
+++ b/src/main/java/org/variantsync/diffdetective/mining/MiningTask.java
@@ -73,7 +73,7 @@ public AnalysisResult call() throws Exception {
                             continue;
                         }
 
-                        LineGraphExport.toLineGraphFormat(patch, exportOptions, lineGraphDestination, miningResult);
+                        miningResult.append(LineGraphExport.toLineGraphFormat(miningResult.repoName, patch, exportOptions, lineGraphDestination));
 
                         t.forAll(node -> {
                             if (node.isArtifact()) {
