Skip to content

Commit 3a17a78

Browse files
committed
Adapt the line graph exporter to the new Exporter interface
Line graph specific export code is moved to the line graph exporter. This changes the line endings to use the OS dependent representation through the use of `PrintStream`. Currently there is an unnecessary conversion from `String` to `ByteArray` and back because `OutputStream`s are used. This will be optimised in the future by directly outputting to the desired devices (file and terminal) without converting back to a `String` representation.
1 parent 285df86 commit 3a17a78

7 files changed

Lines changed: 120 additions & 141 deletions

File tree

src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/DiffTreeLineGraphExporter.java

Lines changed: 40 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,60 @@
11
package org.variantsync.diffdetective.diff.difftree.serialize;
22

3-
import org.variantsync.diffdetective.diff.difftree.DiffNode;
3+
import java.io.OutputStream;
4+
import java.io.PrintStream;
5+
46
import org.variantsync.diffdetective.diff.difftree.DiffTree;
5-
import org.variantsync.diffdetective.util.StringUtils;
7+
import org.variantsync.diffdetective.diff.difftree.LineGraphConstants;
8+
import org.variantsync.functjonal.Functjonal;
69

710
/**
811
* Exporter that converts a single DiffTree's nodes and edges to linegraph.
912
*/
10-
public class DiffTreeLineGraphExporter {
11-
private final StringBuilder nodesString = new StringBuilder();
12-
private final StringBuilder edgesString = new StringBuilder();
13-
14-
private final DiffTree diffTree;
15-
13+
public class DiffTreeLineGraphExporter implements Exporter {
14+
private final Format format;
1615
private final DiffTreeSerializeDebugData debugData;
1716

18-
/**
19-
* Creates a new exporter that will export the given tree.
20-
*/
21-
public DiffTreeLineGraphExporter(DiffTree treeToExport) {
22-
this.diffTree = treeToExport;
17+
public DiffTreeLineGraphExporter(Format format) {
18+
this.format = format;
2319
this.debugData = new DiffTreeSerializeDebugData();
2420
}
2521

22+
public DiffTreeLineGraphExporter(DiffTreeLineGraphExportOptions options) {
23+
this(new Format(options.nodeFormat(), options.edgeFormat()));
24+
}
25+
2626
/**
27-
* Converts the given node and its edges to linegraph using the formats specified in the given options.
28-
* The produced linegraph statements will be added to the internal StringBuilders.
29-
* @param node The node to convert to linegraph format together with its edges.
30-
* @param options Options that specify the node and edge format to use.
27+
* Export a line graph of {@code diffTree} into {@code destination}.
28+
*
29+
* @param diffTree to be exported
30+
* @param destination where the result should be written
3131
*/
32-
private void visit(DiffNode node, DiffTreeLineGraphExportOptions options) {
33-
switch (node.diffType) {
34-
case ADD -> ++debugData.numExportedAddNodes;
35-
case REM -> ++debugData.numExportedRemNodes;
36-
case NON -> ++debugData.numExportedNonNodes;
37-
}
32+
@Override
33+
public void exportDiffTree(DiffTree diffTree, OutputStream destination) {
34+
var output = new PrintStream(destination);
35+
format.forEachNode(diffTree, (node) -> {
36+
switch (node.diffType) {
37+
case ADD -> ++debugData.numExportedAddNodes;
38+
case REM -> ++debugData.numExportedRemNodes;
39+
case NON -> ++debugData.numExportedNonNodes;
40+
}
3841

39-
nodesString
40-
.append(options.nodeFormat().toLineGraphLine(node))
41-
.append(StringUtils.LINEBREAK);
42+
output.println(LineGraphConstants.LG_NODE + " " + node.getID() + " " + format.getNodeFormat().toLabel(node));
43+
});
4244

43-
edgesString
44-
.append(options.edgeFormat().getParentEdgeLines(node));
45-
}
45+
format.forEachUniqueEdge(diffTree, (edges) -> {
46+
output.print(Functjonal.unwords(LineGraphConstants.LG_EDGE, edges.get(0).from().getID(), edges.get(0).to().getID(), ""));
4647

47-
/**
48-
* Export this exporter's tree using the given options.
49-
* This method will return the final linegraph as string.
50-
* The string will contain all linegraph statements for the tree's nodes and edges,
51-
* but not the tree header.
52-
* @param options Options that specify the node and edge format to use.
53-
* @return The linegraph as String.
54-
* @see LineGraphExport#composeTreeInLineGraph
55-
*/
56-
public String export(DiffTreeLineGraphExportOptions options) {
57-
diffTree.forAll(n -> visit(n, options));
58-
final String result = nodesString.toString() + edgesString;
59-
StringUtils.clear(nodesString);
60-
StringUtils.clear(edgesString);
61-
return result;
48+
for (var edge : edges) {
49+
output.print(edge.style().lineGraphType());
50+
}
51+
52+
for (var edge : edges) {
53+
output.print(format.getEdgeFormat().labelOf(edge));
54+
}
55+
56+
output.println();
57+
});
6258
}
6359

6460
/**

src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/Format.java

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.variantsync.diffdetective.diff.difftree.serialize;
22

3+
import java.util.List;
34
import java.util.function.Consumer;
45
import org.variantsync.diffdetective.diff.difftree.DiffNode;
56
import org.variantsync.diffdetective.diff.difftree.DiffTree;
@@ -53,25 +54,86 @@ public void forEachNode(DiffTree diffTree, Consumer<DiffNode> callback) {
5354
* Exporters should use this method to enable subclasses of {@code Format} to filter edges, add
5455
* new edges and change the order of the exported edges.
5556
*
57+
* This implementation uses {@link forEachUniqueEdge} by calling {@code callback} for each edge
58+
* in the order given by the lists of {@link forEachUniqueEdge}.
59+
*
5660
* @param diffTree to be exported
5761
* @param callback is called for each edge
5862
*/
5963
public void forEachEdge(DiffTree diffTree, Consumer<StyledEdge> callback) {
64+
forEachUniqueEdge(diffTree, (edges) -> {
65+
for (var edge : edges) {
66+
callback.accept(edge);
67+
}
68+
});
69+
}
70+
71+
/**
72+
* Iterates over all edges in {@code diffTree} and calls {@code callback}, visiting parallel edges only once.
73+
*
74+
* Two edges are parallel if they start at the same node and end at the same node. Note that
75+
* the direction of directed edges matters.
76+
*
77+
* All parallel edges are collected into a list and are passed once to {@code callback}.
78+
*
79+
* Exporters should use this method to enable subclasses of {@code Format} to filter edges, add
80+
* new edges and change the order of the exported edges.
81+
*
82+
* @param diffTree to be exported
83+
* @param callback is called for each unique edge
84+
*/
85+
public void forEachUniqueEdge(DiffTree diffTree, Consumer<List<StyledEdge>> callback) {
6086
diffTree.forAll((node) -> {
61-
processEdge(node, node.getBeforeParent(), StyledEdge.BEFORE, callback);
62-
processEdge(node, node.getAfterParent(), StyledEdge.AFTER, callback);
87+
var beforeParent = node.getBeforeParent();
88+
var afterParent = node.getAfterParent();
89+
90+
// Are both parent edges the same?
91+
if (beforeParent != null && afterParent != null && beforeParent == afterParent) {
92+
callback.accept(List.of(beforeEdge(node), afterEdge(node)));
93+
} else {
94+
if (beforeParent != null) {
95+
callback.accept(List.of(beforeEdge(node)));
96+
}
97+
if (afterParent != null) {
98+
callback.accept(List.of(afterEdge(node)));
99+
}
100+
}
63101
});
64102
}
65103

66-
private void processEdge(DiffNode node, DiffNode parent, StyledEdge.Style style, Consumer<StyledEdge> callback) {
67-
if (parent == null) {
68-
return;
69-
}
104+
/**
105+
* Constructs a {@link StyledEdge} from {@code node} and its before parent.
106+
*
107+
* The order of these nodes is permuted according to {@link EdgeLabelFormat#getEdgeDirection}
108+
* of {@link getEdgeFormat()}.
109+
*/
110+
protected StyledEdge beforeEdge(DiffNode node) {
111+
return sortedEdgeWithLabel(node, node.getBeforeParent(), StyledEdge.BEFORE);
112+
}
70113

71-
var edge = edgeFormat.getEdgeDirection().sort(node, parent);
72-
callback.accept(new StyledEdge(
73-
edge.first(),
74-
edge.second(),
75-
style));
114+
/**
115+
* Constructs a {@link StyledEdge} from {@code node} and its after parent.
116+
*
117+
* The order of these nodes is permuted according to {@link EdgeLabelFormat#getEdgeDirection}
118+
* of {@link getEdgeFormat()}.
119+
*/
120+
protected StyledEdge afterEdge(DiffNode node) {
121+
return sortedEdgeWithLabel(node, node.getAfterParent(), StyledEdge.AFTER);
122+
}
123+
124+
/**
125+
* Constructs a {@link StyledEdge} from {@code originalFrom} to {@code originalTo}.
126+
*
127+
* The order of these nodes is permuted according to {@link EdgeLabelFormat#getEdgeDirection}
128+
* of {@link getEdgeFormat()}.
129+
*
130+
* @param originalFrom the origin of the constructed edge
131+
* @param originalTo the destination of the constructed edge
132+
* @param style the export style of the constructed edge
133+
* @return a new {@link StyledEdge}
134+
*/
135+
protected StyledEdge sortedEdgeWithLabel(DiffNode originalFrom, DiffNode originalTo, StyledEdge.Style style) {
136+
var edge = edgeFormat.getEdgeDirection().sort(originalFrom, originalTo);
137+
return new StyledEdge(edge.first(), edge.second(), style);
76138
}
77139
}

src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/LineGraphExport.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.variantsync.diffdetective.diff.difftree.serialize;
22

3+
import java.io.ByteArrayOutputStream;
4+
35
import org.tinylog.Logger;
46
import org.variantsync.diffdetective.analysis.AnalysisResult;
57
import org.variantsync.diffdetective.diff.CommitDiff;
@@ -29,9 +31,10 @@ public static Pair<DiffTreeSerializeDebugData, String> toLineGraphFormat(final D
2931
diffTree.assertConsistency();
3032

3133
if (options.treeFilter().test(diffTree)) {
32-
final DiffTreeLineGraphExporter exporter = new DiffTreeLineGraphExporter(diffTree);
33-
final String result = exporter.export(options);
34-
return new Pair<>(exporter.getDebugData(), result);
34+
final var exporter = new DiffTreeLineGraphExporter(options);
35+
var output = new ByteArrayOutputStream();
36+
exporter.exportDiffTree(diffTree, output);
37+
return new Pair<>(exporter.getDebugData(), output.toString());
3538
}
3639

3740
return null;

src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/edgeformat/DefaultEdgeLabelFormat.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package org.variantsync.diffdetective.diff.difftree.serialize.edgeformat;
22

33
import org.variantsync.diffdetective.diff.difftree.DiffNode;
4-
import org.variantsync.diffdetective.diff.difftree.LineGraphConstants;
54
import org.variantsync.diffdetective.diff.difftree.serialize.StyledEdge;
6-
import org.variantsync.functjonal.Functjonal;
75

86
/**
97
* Default implementation of {@link EdgeLabelFormat}.
@@ -26,11 +24,6 @@ public DefaultEdgeLabelFormat(final EdgeLabelFormat.Direction direction) {
2624
super(direction);
2725
}
2826

29-
@Override
30-
public String edgeToLineGraph(DiffNode from, DiffNode to, String labelPrefix) {
31-
return Functjonal.unwords(LineGraphConstants.LG_EDGE, from.getID(), to.getID(), labelPrefix);
32-
}
33-
3427
@Override
3528
public String labelOf(StyledEdge edge) {
3629
return "";

src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/edgeformat/EdgeLabelFormat.java

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import org.variantsync.diffdetective.diff.difftree.serialize.StyledEdge;
66
import org.variantsync.diffdetective.diff.difftree.serialize.LinegraphFormat;
77
import org.variantsync.diffdetective.util.Assert;
8-
import org.variantsync.diffdetective.util.StringUtils;
98
import org.variantsync.functjonal.Pair;
109

1110
import java.util.List;
@@ -142,55 +141,6 @@ public void connect(final String lineGraphLine, final Map<Integer, DiffNode> nod
142141
connectAccordingToLabel(childNode, parentNode, name);
143142
}
144143

145-
/**
146-
* Serializes the edges from given node to its parent
147-
* to a string of lines, where each edge is placed on one line.
148-
*
149-
* @param node The {@link DiffNode} whose edges to parents to export.
150-
* @return Linegraph lines for each edge from the given node to its parents. All lines are put into the same string and separated by a line break ("\n").
151-
*/
152-
public String getParentEdgeLines(final DiffNode node) {
153-
final DiffNode beforeParent = node.getBeforeParent();
154-
final DiffNode afterParent = node.getAfterParent();
155-
final boolean hasBeforeParent = beforeParent != null;
156-
final boolean hasAfterParent = afterParent != null;
157-
158-
StringBuilder edgesString = new StringBuilder();
159-
// If the node has exactly one parent
160-
if (hasBeforeParent && hasAfterParent && beforeParent == afterParent) {
161-
edgesString
162-
.append(edgeToLineGraphSorted(node, beforeParent, LineGraphConstants.BEFORE_AND_AFTER_PARENT))
163-
.append(StringUtils.LINEBREAK);
164-
} else {
165-
if (hasBeforeParent) {
166-
edgesString
167-
.append(edgeToLineGraphSorted(node, beforeParent, LineGraphConstants.BEFORE_PARENT))
168-
.append(StringUtils.LINEBREAK);
169-
}
170-
if (hasAfterParent) {
171-
edgesString
172-
.append(edgeToLineGraphSorted(node, afterParent, LineGraphConstants.AFTER_PARENT))
173-
.append(StringUtils.LINEBREAK);
174-
}
175-
}
176-
return edgesString.toString();
177-
}
178-
179-
private String edgeToLineGraphSorted(DiffNode desiredFrom, DiffNode desiredTo, final String labelPrefix) {
180-
final Pair<DiffNode, DiffNode> sorted = edgeDirection.sort(desiredFrom, desiredTo);
181-
return edgeToLineGraph(sorted.first(), sorted.second(), labelPrefix);
182-
}
183-
184-
/**
185-
* Creates a linegraph edge in the direction from -> to.
186-
* The edge's label should be prefixed by the given prefix.
187-
* @param from Node the edge begins at.
188-
* @param to Node the edge ends at.
189-
* @param labelPrefix Prefix for the produced edge's label.
190-
* @return A line for a linegraph file that describes the given edge.
191-
*/
192-
protected abstract String edgeToLineGraph(DiffNode from, DiffNode to, final String labelPrefix);
193-
194144
/**
195145
* Converts a {@link StyledEdge} into a label suitable for exporting.
196146
* This may be human readable text or machine parseable metadata.

src/main/java/org/variantsync/diffdetective/diff/difftree/serialize/nodeformat/DiffNodeLabelFormat.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,4 @@ default Pair<Integer, DiffNode> fromLineGraphLine(final String lineGraphLine) {
6868
final String label = lineGraphLine.substring(idEnd + 1);
6969
return new Pair<>(nodeId, fromLabelAndId(label, nodeId));
7070
}
71-
72-
/**
73-
* Serializes the given node to a line in linegraph format.
74-
*
75-
* @param node The {@link DiffNode} to be converted
76-
* @return The entire line graph line of a {@link DiffNode}.
77-
*/
78-
default String toLineGraphLine(final DiffNode node) {
79-
return LineGraphConstants.LG_NODE + " " + node.getID() + " " + toLabel(node);
80-
}
81-
}
71+
}

src/main/java/org/variantsync/diffdetective/mining/formats/DirectedEdgeLabelFormat.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.variantsync.diffdetective.mining.formats;
22

33
import org.variantsync.diffdetective.diff.difftree.DiffNode;
4-
import org.variantsync.diffdetective.diff.difftree.LineGraphConstants;
54
import org.variantsync.diffdetective.diff.difftree.serialize.StyledEdge;
65
import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat;
76
import org.variantsync.functjonal.Functjonal;
@@ -52,20 +51,6 @@ protected void connectAccordingToLabel(DiffNode child, DiffNode parent, String e
5251
super.connectAccordingToLabel(child, parent, edgeLabel);
5352
}
5453

55-
protected String edgeToLineGraph(DiffNode from, DiffNode to, final String label) {
56-
return Functjonal.unwords(
57-
LineGraphConstants.LG_EDGE,
58-
from.getID(),
59-
to.getID(),
60-
Functjonal.intercalate(
61-
LABEL_SEPARATOR,
62-
label,
63-
nodeFormatter.toLabel(from),
64-
nodeFormatter.toLabel(to)
65-
)
66-
);
67-
}
68-
6954
@Override
7055
public String labelOf(StyledEdge edge) {
7156
return Functjonal.intercalate(

0 commit comments

Comments
 (0)