package StevenDimDoors.experimental; /** * Provides a complete implementation of a directed graph. * @author SenseiKiwi * * @param The type of data to store in the graph's nodes * @param The type of data to store in the graph's edges */ public class DirectedGraph { private static class GraphNode implements IGraphNode { private LinkedList> inbound; private LinkedList> outbound; private ILinkedListNode> graphEntry; private P data; public GraphNode(P data, LinkedList> graphList) { this.data = data; this.inbound = new LinkedList>(); this.outbound = new LinkedList>(); this.graphEntry = graphList.addLast(this); } public int indegree() { return inbound.size(); } public int outdegree() { return outbound.size(); } public Iterable> inbound() { return inbound; } public Iterable> outbound() { return outbound; } public P data() { return data; } public void remove() { graphEntry.remove(); graphEntry = null; for (Edge edge : inbound) edge.remove(); for (Edge edge : outbound) edge.remove(); inbound = null; outbound = null; data = null; } } private static class Edge implements IEdge { private GraphNode head; private GraphNode tail; private ILinkedListNode> headEntry; private ILinkedListNode> tailEntry; private ILinkedListNode> graphEntry; private Q data; public Edge(GraphNode head, GraphNode tail, Q data, LinkedList> graphList) { this.head = head; this.tail = tail; this.data = data; this.graphEntry = graphList.addLast(this); this.headEntry = head.outbound.addLast(this); this.tailEntry = tail.inbound.addLast(this); } public IGraphNode head() { return head; } public IGraphNode tail() { return tail; } public Q data() { return data; } public void remove() { headEntry.remove(); tailEntry.remove(); graphEntry.remove(); headEntry = null; tailEntry = null; graphEntry = null; head = null; tail = null; data = null; } } private LinkedList> nodes; private LinkedList> edges; public DirectedGraph() { nodes = new LinkedList>(); edges = new LinkedList>(); } public int nodeCount() { return nodes.size(); } public int edgeCount() { return edges.size(); } public boolean isEmpty() { return nodes.isEmpty(); } public Iterable> nodes() { return nodes; } public Iterable> edges() { return edges; } private GraphNode checkNode(IGraphNode node) { GraphNode innerNode = (GraphNode) node; // Check that this node actually belongs to this graph instance. // Accepting foreign nodes could corrupt the graph's internal state. if (innerNode.graphEntry.owner() != nodes) { throw new IllegalArgumentException("The specified node does not belong to this graph."); } return innerNode; } private Edge checkEdge(IEdge edge) { Edge innerEdge = (Edge) edge; // Check that this node actually belongs to this graph instance. // Accepting foreign nodes could corrupt the graph's internal state. if (innerEdge.graphEntry.owner() != edges) { throw new IllegalArgumentException("The specified edge does not belong to this graph."); } return innerEdge; } public IGraphNode addNode(U data) { return new GraphNode(data, nodes); } public IEdge addEdge(IGraphNode head, IGraphNode tail, V data) { GraphNode innerHead = checkNode(head); GraphNode innerTail = checkNode(tail); return new Edge(innerHead, innerTail, data, edges); } public U removeNode(IGraphNode node) { GraphNode innerNode = checkNode(node); U data = innerNode.data(); innerNode.remove(); return data; } public V removeEdge(IEdge edge) { Edge innerEdge = checkEdge(edge); V data = innerEdge.data(); innerEdge.remove(); return data; } public IEdge findEdge(IGraphNode head, IGraphNode tail) { for (IEdge edge : head.outbound()) { if (edge.tail() == tail) return edge; } return null; } public void clear() { // Remove each node individually to guarantee that all external // references are invalidated. That'll prevent memory leaks and // keep external code from using removed nodes or edges. for (GraphNode node : nodes) { node.remove(); } } }