cdc.graphs.core.GraphEquivalence Maven / Gradle / Ivy
package cdc.graphs.core;
import java.util.HashSet;
import java.util.Set;
import cdc.graphs.GraphAdapter;
/**
* Comparison of 2 graphs.
*
* Nodes and edges can be compared using an {@link Informer}.
* All nodes MUST be distinct.
* All edges MUST be distinct.
* The edge constraint could be relaxed someday.
*
* {@link Informer} allows to define specific hash code and equality computation.
* It may be sometimes necessary to use predefined equals and hashCode for nods and edges,
* which is inappropriate for graph comparison.
*
* @author Damien Carbonne
*
* @param The node type.
* @param The edge type.
*/
public class GraphEquivalence {
private final Informer super N> nodesInformer;
private final Informer super E> edgesInformer;
public GraphEquivalence(Informer super N> nodesInformer,
Informer super E> edgesInformer) {
this.nodesInformer = nodesInformer;
this.edgesInformer = edgesInformer;
}
private Set wrapNodes(GraphAdapter adapter) {
final Set wrappers = new HashSet<>();
for (final N node : adapter.getNodes()) {
wrappers.add(new NodeWrapper(node));
}
return wrappers;
}
private Set wrapEdges(GraphAdapter adapter) {
final Set wrappers = new HashSet<>();
for (final E edge : adapter.getEdges()) {
wrappers.add(new EdgeWrapper(edge));
}
return wrappers;
}
private static Set added(Set left,
Set right) {
final Set set = new HashSet<>(right);
set.removeAll(left);
return set;
}
private static Set removed(Set left,
Set right) {
final Set set = new HashSet<>(left);
set.removeAll(right);
return set;
}
public GraphDiff compare(GraphAdapter left,
GraphAdapter right) {
final Set leftNodes = wrapNodes(left);
final Set leftEdges = wrapEdges(left);
final Set rightNodes = wrapNodes(right);
final Set rightEdges = wrapEdges(right);
final Set addedNodes = added(leftNodes, rightNodes);
final Set removedNodes = removed(leftNodes, rightNodes);
final Set addedEdges = added(leftEdges, rightEdges);
final Set removedEdges = removed(leftEdges, rightEdges);
final GraphDiff diffs = new GraphDiff<>();
for (final NodeWrapper wrapper : addedNodes) {
diffs.addAddedNode(wrapper.delegate);
}
for (final NodeWrapper wrapper : removedNodes) {
diffs.addRemovedNode(wrapper.delegate);
}
for (final EdgeWrapper wrapper : addedEdges) {
diffs.addAddedEdge(wrapper.delegate);
}
for (final EdgeWrapper wrapper : removedEdges) {
diffs.addRemovedEdge(wrapper.delegate);
}
return diffs;
}
/**
* Interface used to extract comparison information on an object.
*
* @param The object type.
*/
public static interface Informer {
/**
* @param object The object.
* @return The hash code of {@code object}. It may differ from {@code object.hashCode()}.
*/
public int hashCode(T object);
/**
*
* @param o1 The first object.
* @param o2 The second object.
* @return {@code true} if {@code o1} and {@code o2} are equal.
* This may differ from {@code o1.equals(o2)}
* or {@code o2.equals(o1)}.
*/
public boolean areEqual(T o1,
T o2);
}
class NodeWrapper {
private final N delegate;
NodeWrapper(N delegate) {
this.delegate = delegate;
}
@Override
public int hashCode() {
return GraphEquivalence.this.nodesInformer.hashCode(delegate);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof GraphEquivalence.NodeWrapper)) {
return false;
}
@SuppressWarnings("unchecked")
final GraphEquivalence.NodeWrapper other = (GraphEquivalence.NodeWrapper) object;
return GraphEquivalence.this.nodesInformer.areEqual(delegate, other.delegate);
}
}
class EdgeWrapper {
private final E delegate;
EdgeWrapper(E delegate) {
this.delegate = delegate;
}
@Override
public int hashCode() {
return GraphEquivalence.this.edgesInformer.hashCode(delegate);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof GraphEquivalence.NodeWrapper)) {
return false;
}
@SuppressWarnings("unchecked")
final GraphEquivalence.EdgeWrapper other = (GraphEquivalence.EdgeWrapper) object;
return GraphEquivalence.this.edgesInformer.areEqual(delegate, other.delegate);
}
}
}