|
| 1 | +package org.variantsync.diffdetective.diff.difftree.serialize; |
| 2 | + |
| 3 | +import java.util.List; |
| 4 | +import java.util.function.Consumer; |
| 5 | +import org.variantsync.diffdetective.diff.difftree.DiffNode; |
| 6 | +import org.variantsync.diffdetective.diff.difftree.DiffTree; |
| 7 | +import org.variantsync.diffdetective.diff.difftree.serialize.edgeformat.EdgeLabelFormat; |
| 8 | +import org.variantsync.diffdetective.diff.difftree.serialize.nodeformat.DiffNodeLabelFormat; |
| 9 | + |
| 10 | +/** |
| 11 | + * Format used for exporting a {@link DiffTree}. |
| 12 | + * For easy reusability this class is composed of separate node and edge formats. |
| 13 | + * |
| 14 | + * The exported {@link DiffTree} can be influenced in the following ways: |
| 15 | + * - Providing both a node and an edge label format. |
| 16 | + * - Changing the order, filtering or adding the nodes and edges by creating a subclass of {@code |
| 17 | + * Format}. |
| 18 | + */ |
| 19 | +public class Format { |
| 20 | + private final DiffNodeLabelFormat nodeFormat; |
| 21 | + private final EdgeLabelFormat edgeFormat; |
| 22 | + |
| 23 | + public Format(DiffNodeLabelFormat nodeFormat, EdgeLabelFormat edgeFormat) { |
| 24 | + this.nodeFormat = nodeFormat; |
| 25 | + this.edgeFormat = edgeFormat; |
| 26 | + } |
| 27 | + |
| 28 | + public DiffNodeLabelFormat getNodeFormat() { |
| 29 | + return nodeFormat; |
| 30 | + } |
| 31 | + |
| 32 | + public EdgeLabelFormat getEdgeFormat() { |
| 33 | + return edgeFormat; |
| 34 | + } |
| 35 | + |
| 36 | + /** |
| 37 | + * Iterates over all {@link DiffNode}s in {@code diffTree} and calls {@code callback}. |
| 38 | + * |
| 39 | + * Exporters should use this method to enable subclasses of {@code Format} to filter nodes, add |
| 40 | + * new nodes and change the order of the exported nodes. |
| 41 | + * |
| 42 | + * This implementation is equivalent to {@link DiffTree#forAll}. |
| 43 | + * |
| 44 | + * @param diffTree to be exported |
| 45 | + * @param callback is called for each node |
| 46 | + */ |
| 47 | + public void forEachNode(DiffTree diffTree, Consumer<DiffNode> callback) { |
| 48 | + diffTree.forAll(callback); |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * Iterates over all edges in {@code diffTree} and calls {@code callback}. |
| 53 | + * |
| 54 | + * Exporters should use this method to enable subclasses of {@code Format} to filter edges, add |
| 55 | + * new edges and change the order of the exported edges. |
| 56 | + * |
| 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 | + * |
| 60 | + * @param diffTree to be exported |
| 61 | + * @param callback is called for each edge |
| 62 | + */ |
| 63 | + 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) { |
| 86 | + diffTree.forAll((node) -> { |
| 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 | + } |
| 101 | + }); |
| 102 | + } |
| 103 | + |
| 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 | + } |
| 113 | + |
| 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); |
| 138 | + } |
| 139 | +} |
0 commit comments