/** * Author: Georg Hofferek <georg.hofferek@iaik.tugraz.at> */ package at.iaik.suraq.util.graph; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * A simple graph with generic nodes. * * @author Georg Hofferek <georg.hofferek@iaik.tugraz.at> * */ public class Graph<N, A> { private final Set<N> nodes = new HashSet<N>(); private final Set<AnnotatedEdge<N, A>> edges = new HashSet<AnnotatedEdge<N, A>>(); private final boolean directed; public Graph() { this(false); } public Graph(boolean directed) { this.directed = directed; } public void addNode(N node) { if (!nodes.contains(node)) { nodes.add(node); } } public void addEdge(N src, N dst) { this.addEdge(src, dst, null); } public void addEdge(N src, N dst, A annotation) { this.addNode(src); this.addNode(dst); this.addDirectedEdge(src, dst, annotation); if (!this.directed) this.addDirectedEdge(dst, src, annotation); } private void addDirectedEdge(N src, N dst, A annotation) { assert (nodes.contains(src)); assert (nodes.contains(dst)); edges.add(new AnnotatedEdge<N, A>(src, dst, annotation)); } /** * Finds a path from <code>src</code> to <code>dst</code> in the graph and * returns the annotations of the edges along the path. * * @param src * @param dst * @return Annotations along a path from <code>src</code> to * <code>dst</code>, or <code>null</code> if no such path exists. */ public List<A> findPath(N src, N dst) { List<AnnotatedEdge<N, A>> path = new ArrayList<AnnotatedEdge<N, A>>(); if (!nodes.contains(src) || !nodes.contains(dst)) return null; for (AnnotatedEdge<N, A> edge : edges) { if (!edge.getSrc().equals(src)) continue; path.add(edge); if (edge.getDst().equals(dst)) break; path = this.findPath(path, dst); if (path.size() > 0) break; } if (path.size() == 0) return null; List<A> result = new ArrayList<A>(path.size()); for (AnnotatedEdge<N, A> edge : path) result.add(edge.getAnnotation()); return result; } private List<AnnotatedEdge<N, A>> findPath( List<AnnotatedEdge<N, A>> prefix, N dst) { List<AnnotatedEdge<N, A>> result; Set<N> visited = new HashSet<N>(); for (AnnotatedEdge<N, A> edge : prefix) { visited.add(edge.getSrc()); visited.add(edge.getDst()); } N current = prefix.get(prefix.size() - 1).getDst(); for (AnnotatedEdge<N, A> edge : edges) { if (!edge.getSrc().equals(current)) continue; if (edge.getDst().equals(dst)) { result = new ArrayList<AnnotatedEdge<N, A>>(prefix); result.add(edge); return result; } if (visited.contains(edge.getDst())) continue; prefix.add(edge); List<AnnotatedEdge<N, A>> path = findPath(prefix, dst); if (path.size() > 0) { assert (path.get(path.size() - 1).getDst().equals(dst)); return path; } prefix.remove(edge); } return new ArrayList<AnnotatedEdge<N, A>>(0); } /** * * @return a copy of the set of nodes. */ public Set<N> getNodes() { Set<N> result = new HashSet<N>(nodes); return result; } @Override public String toString() { StringBuilder builder = new StringBuilder(nodes.size() * 20 + edges.size() * 50); builder.append(this.directed ? "Directed " : "Undirected "); builder.append("Graph:\n("); builder.append(nodes.size()); builder.append(" nodes, "); builder.append(edges.size()); builder.append(" edges)\n\n"); builder.append(nodes.size()); builder.append(" Nodes\n"); for (N node : nodes) { builder.append(node.toString().replaceAll("\\s{2,}", " ") .replace("\n", "")); builder.append("\n"); } builder.append("\n"); builder.append(edges.size()); builder.append(" Edges"); builder.append("\n"); for (AnnotatedEdge<N, A> edge : edges) { builder.append(edge.getSrc().toString().replaceAll("\\s{2,}", " ") .replace("\n", "")); builder.append(" --- "); builder.append(edge.getAnnotation().toString()); builder.append(" ---> "); builder.append(edge.getDst().toString().replaceAll("\\s{2,}", " ") .replace("\n", "")); builder.append("\n"); } return builder.toString(); } }