Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ name: Java CI with Maven

on:
push:
branches: [ "main" ]
branches: [ "main", "develop" ]
pull_request:
branches: [ "main" ]
branches: [ "main", "develop" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- name: Install Graphviz
run: sudo apt-get install -y graphviz
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,13 @@ public boolean isAdd() {
return this.diffType.equals(DiffType.ADD);
}

/**
* Returns the diff type of this node.
*/
public DiffType getDiffType() {
return this.diffType;
}

/**
* Returns true if this node represents an ELIF macro.
* @see CodeType#ELIF
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,60 @@
package org.variantsync.diffdetective.diff.difftree.serialize;

import org.variantsync.diffdetective.diff.difftree.DiffNode;
import java.io.OutputStream;
import java.io.PrintStream;

import org.variantsync.diffdetective.diff.difftree.DiffTree;
import org.variantsync.diffdetective.util.StringUtils;
import org.variantsync.diffdetective.diff.difftree.LineGraphConstants;
import org.variantsync.functjonal.Functjonal;

/**
* Exporter that converts a single DiffTree's nodes and edges to linegraph.
*/
public class DiffTreeLineGraphExporter {
private final StringBuilder nodesString = new StringBuilder();
private final StringBuilder edgesString = new StringBuilder();

private final DiffTree diffTree;

public class DiffTreeLineGraphExporter implements Exporter {
private final Format format;
private final DiffTreeSerializeDebugData debugData;

/**
* Creates a new exporter that will export the given tree.
*/
public DiffTreeLineGraphExporter(DiffTree treeToExport) {
this.diffTree = treeToExport;
public DiffTreeLineGraphExporter(Format format) {
this.format = format;
this.debugData = new DiffTreeSerializeDebugData();
}

public DiffTreeLineGraphExporter(DiffTreeLineGraphExportOptions options) {
this(new Format(options.nodeFormat(), options.edgeFormat()));
}

/**
* Converts the given node and its edges to linegraph using the formats specified in the given options.
* The produced linegraph statements will be added to the internal StringBuilders.
* @param node The node to convert to linegraph format together with its edges.
* @param options Options that specify the node and edge format to use.
* Export a line graph of {@code diffTree} into {@code destination}.
*
* @param diffTree to be exported
* @param destination where the result should be written
*/
private void visit(DiffNode node, DiffTreeLineGraphExportOptions options) {
switch (node.diffType) {
case ADD -> ++debugData.numExportedAddNodes;
case REM -> ++debugData.numExportedRemNodes;
case NON -> ++debugData.numExportedNonNodes;
}
@Override
public void exportDiffTree(DiffTree diffTree, OutputStream destination) {
var output = new PrintStream(destination);
format.forEachNode(diffTree, (node) -> {
switch (node.diffType) {
case ADD -> ++debugData.numExportedAddNodes;
case REM -> ++debugData.numExportedRemNodes;
case NON -> ++debugData.numExportedNonNodes;
}

nodesString
.append(options.nodeFormat().toLineGraphLine(node))
.append(StringUtils.LINEBREAK);
output.println(LineGraphConstants.LG_NODE + " " + node.getID() + " " + format.getNodeFormat().toLabel(node));
});

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

/**
* Export this exporter's tree using the given options.
* This method will return the final linegraph as string.
* The string will contain all linegraph statements for the tree's nodes and edges,
* but not the tree header.
* @param options Options that specify the node and edge format to use.
* @return The linegraph as String.
* @see LineGraphExport#composeTreeInLineGraph
*/
public String export(DiffTreeLineGraphExportOptions options) {
diffTree.forAll(n -> visit(n, options));
final String result = nodesString.toString() + edgesString;
StringUtils.clear(nodesString);
StringUtils.clear(edgesString);
return result;
for (var edge : edges) {
output.print(edge.style().lineGraphType());
}

for (var edge : edges) {
output.print(format.getEdgeFormat().labelOf(edge));
}

output.println();
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.variantsync.diffdetective.diff.difftree.serialize;

import java.io.IOException;
import java.io.OutputStream;
import org.variantsync.diffdetective.diff.difftree.DiffTree;

/**
* Common interface for serialisation of a single {@code DiffTree}.
* Not all formats have to provide a way to deserialize a {@link DiffTree} from this format.
*
* @author Benjamin Moosherr
*/
public interface Exporter {
/**
* Export a {@code diffTree} into {@code destination}.
*
* This method should have no side effects besides writing to {@code destination}. Above all,
* {@code diffTree} shouldn't be modified. Furthermore, {@code destination} shouldn't be
* closed to allow the embedding of the exported format into a surrounding file.
*
* It can be assumed, that {@code destination} is sufficiently buffered.
*
* @param diffTree to be exported
* @param destination where the result should be written
*/
void exportDiffTree(DiffTree diffTree, OutputStream destination) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.variantsync.diffdetective.diff.difftree.serialize;

import java.util.List;
import java.util.function.Consumer;
import org.variantsync.diffdetective.diff.difftree.DiffNode;
import org.variantsync.diffdetective.diff.difftree.DiffTree;
import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat;
import org.variantsync.diffdetective.diff.difftree.serialize.nodeformat.DiffNodeLabelFormat;

/**
* Format used for exporting a {@link DiffTree}.
* For easy reusability this class is composed of separate node and edge formats.
*
* The exported {@link DiffTree} can be influenced in the following ways:
* - Providing both a node and an edge label format.
* - Changing the order, filtering or adding the nodes and edges by creating a subclass of {@code
* Format}.
*/
public class Format {
private final DiffNodeLabelFormat nodeFormat;
private final EdgeLabelFormat edgeFormat;

public Format(DiffNodeLabelFormat nodeFormat, EdgeLabelFormat edgeFormat) {
this.nodeFormat = nodeFormat;
this.edgeFormat = edgeFormat;
}

public DiffNodeLabelFormat getNodeFormat() {
return nodeFormat;
}

public EdgeLabelFormat getEdgeFormat() {
return edgeFormat;
}

/**
* Iterates over all {@link DiffNode}s in {@code diffTree} and calls {@code callback}.
*
* Exporters should use this method to enable subclasses of {@code Format} to filter nodes, add
* new nodes and change the order of the exported nodes.
*
* This implementation is equivalent to {@link DiffTree#forAll}.
*
* @param diffTree to be exported
* @param callback is called for each node
*/
public void forEachNode(DiffTree diffTree, Consumer<DiffNode> callback) {
diffTree.forAll(callback);
}

/**
* Iterates over all edges in {@code diffTree} and calls {@code callback}.
*
* Exporters should use this method to enable subclasses of {@code Format} to filter edges, add
* new edges and change the order of the exported edges.
*
* This implementation uses {@link forEachUniqueEdge} by calling {@code callback} for each edge
* in the order given by the lists of {@link forEachUniqueEdge}.
*
* @param diffTree to be exported
* @param callback is called for each edge
*/
public void forEachEdge(DiffTree diffTree, Consumer<StyledEdge> callback) {
forEachUniqueEdge(diffTree, (edges) -> {
for (var edge : edges) {
callback.accept(edge);
}
});
}

/**
* Iterates over all edges in {@code diffTree} and calls {@code callback}, visiting parallel edges only once.
*
* Two edges are parallel if they start at the same node and end at the same node. Note that
* the direction of directed edges matters.
*
* All parallel edges are collected into a list and are passed once to {@code callback}.
*
* Exporters should use this method to enable subclasses of {@code Format} to filter edges, add
* new edges and change the order of the exported edges.
*
* @param diffTree to be exported
* @param callback is called for each unique edge
*/
public void forEachUniqueEdge(DiffTree diffTree, Consumer<List<StyledEdge>> callback) {
diffTree.forAll((node) -> {
var beforeParent = node.getBeforeParent();
var afterParent = node.getAfterParent();

// Are both parent edges the same?
if (beforeParent != null && afterParent != null && beforeParent == afterParent) {
callback.accept(List.of(beforeEdge(node), afterEdge(node)));
} else {
if (beforeParent != null) {
callback.accept(List.of(beforeEdge(node)));
}
if (afterParent != null) {
callback.accept(List.of(afterEdge(node)));
}
}
});
}

/**
* Constructs a {@link StyledEdge} from {@code node} and its before parent.
*
* The order of these nodes is permuted according to {@link EdgeLabelFormat#getEdgeDirection}
* of {@link getEdgeFormat()}.
*/
protected StyledEdge beforeEdge(DiffNode node) {
return sortedEdgeWithLabel(node, node.getBeforeParent(), StyledEdge.BEFORE);
}

/**
* Constructs a {@link StyledEdge} from {@code node} and its after parent.
*
* The order of these nodes is permuted according to {@link EdgeLabelFormat#getEdgeDirection}
* of {@link getEdgeFormat()}.
*/
protected StyledEdge afterEdge(DiffNode node) {
return sortedEdgeWithLabel(node, node.getAfterParent(), StyledEdge.AFTER);
}

/**
* Constructs a {@link StyledEdge} from {@code originalFrom} to {@code originalTo}.
*
* The order of these nodes is permuted according to {@link EdgeLabelFormat#getEdgeDirection}
* of {@link getEdgeFormat()}.
*
* @param originalFrom the origin of the constructed edge
* @param originalTo the destination of the constructed edge
* @param style the export style of the constructed edge
* @return a new {@link StyledEdge}
*/
protected StyledEdge sortedEdgeWithLabel(DiffNode originalFrom, DiffNode originalTo, StyledEdge.Style style) {
var edge = edgeFormat.getEdgeDirection().sort(originalFrom, originalTo);
return new StyledEdge(edge.first(), edge.second(), style);
}
}
Loading