--- /dev/null
+/*
+* Copyright (c) 2003, the JUNG Project and the Regents of the University
+* of California
+* All rights reserved.
+*
+* This software is open-source under the BSD license; see either
+* "license.txt" or
+* http://jung.sourceforge.net/license.txt for a description.
+*/
+package edu.uci.ics.jung.algorithms.shortestpath;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections15.Transformer;
+
+import edu.uci.ics.jung.graph.Graph;
+
+/**
+ * <p>Calculates distances and shortest paths using Dijkstra's
+ * single-source-shortest-path algorithm. This is a lightweight
+ * extension of <code>DijkstraDistance</code> that also stores
+ * path information, so that the shortest paths can be reconstructed.</p>
+ *
+ * <p> The elements in the maps returned by
+ * <code>getIncomingEdgeMap</code> are ordered (that is, returned
+ * by the iterator) by nondecreasing distance from <code>source</code>.</p>
+ *
+ * @author Joshua O'Madadhain
+ * @author Tom Nelson converted to jung2
+ * @see DijkstraDistance
+ */
+public class DijkstraShortestPath<V,E> extends DijkstraDistance<V,E> implements ShortestPath<V,E>
+{
+ /**
+ * <p>Creates an instance of <code>DijkstraShortestPath</code> for
+ * the specified graph and the specified method of extracting weights
+ * from edges, which caches results locally if and only if
+ * <code>cached</code> is <code>true</code>.
+ *
+ * @param g the graph on which distances will be calculated
+ * @param nev the class responsible for returning weights for edges
+ * @param cached specifies whether the results are to be cached
+ */
+ public DijkstraShortestPath(Graph<V,E> g, Transformer<E, ? extends Number> nev, boolean cached)
+ {
+ super(g, nev, cached);
+ }
+
+ /**
+ * <p>Creates an instance of <code>DijkstraShortestPath</code> for
+ * the specified graph and the specified method of extracting weights
+ * from edges, which caches results locally.
+ *
+ * @param g the graph on which distances will be calculated
+ * @param nev the class responsible for returning weights for edges
+ */
+ public DijkstraShortestPath(Graph<V,E> g, Transformer<E, ? extends Number> nev)
+ {
+ super(g, nev);
+ }
+
+ /**
+ * <p>Creates an instance of <code>DijkstraShortestPath</code> for
+ * the specified unweighted graph (that is, all weights 1) which
+ * caches results locally.
+ *
+ * @param g the graph on which distances will be calculated
+ */
+ public DijkstraShortestPath(Graph<V,E> g)
+ {
+ super(g);
+ }
+
+ /**
+ * <p>Creates an instance of <code>DijkstraShortestPath</code> for
+ * the specified unweighted graph (that is, all weights 1) which
+ * caches results locally.
+ *
+ * @param g the graph on which distances will be calculated
+ * @param cached specifies whether the results are to be cached
+ */
+ public DijkstraShortestPath(Graph<V,E> g, boolean cached)
+ {
+ super(g, cached);
+ }
+
+ @Override
+ protected SourceData getSourceData(V source)
+ {
+ SourceData sd = sourceMap.get(source);
+ if (sd == null)
+ sd = new SourcePathData(source);
+ return sd;
+ }
+
+ /**
+ * <p>Returns the last edge on a shortest path from <code>source</code>
+ * to <code>target</code>, or null if <code>target</code> is not
+ * reachable from <code>source</code>.</p>
+ *
+ * <p>If either vertex is not in the graph for which this instance
+ * was created, throws <code>IllegalArgumentException</code>.</p>
+ */
+ public E getIncomingEdge(V source, V target)
+ {
+ if (!g.containsVertex(source))
+ throw new IllegalArgumentException("Specified source vertex " +
+ source + " is not part of graph " + g);
+
+ if (!g.containsVertex(target))
+ throw new IllegalArgumentException("Specified target vertex " +
+ target + " is not part of graph " + g);
+
+ Set<V> targets = new HashSet<V>();
+ targets.add(target);
+ singleSourceShortestPath(source, targets, g.getVertexCount());
+ Map<V,E> incomingEdgeMap =
+ ((SourcePathData)sourceMap.get(source)).incomingEdges;
+ E incomingEdge = incomingEdgeMap.get(target);
+
+ if (!cached)
+ reset(source);
+
+ return incomingEdge;
+ }
+
+ /**
+ * <p>Returns a <code>LinkedHashMap</code> which maps each vertex
+ * in the graph (including the <code>source</code> vertex)
+ * to the last edge on the shortest path from the
+ * <code>source</code> vertex.
+ * The map's iterator will return the elements in order of
+ * increasing distance from <code>source</code>.</p>
+ *
+ * @see DijkstraDistance#getDistanceMap(Object,int)
+ * @see DijkstraDistance#getDistance(Object,Object)
+ * @param source the vertex from which distances are measured
+ */
+ public Map<V,E> getIncomingEdgeMap(V source)
+ {
+ return getIncomingEdgeMap(source, g.getVertexCount());
+ }
+
+ /**
+ * Returns a <code>List</code> of the edges on the shortest path from
+ * <code>source</code> to <code>target</code>, in order of their
+ * occurrence on this path.
+ * If either vertex is not in the graph for which this instance
+ * was created, throws <code>IllegalArgumentException</code>.
+ */
+ private List<E> getPath(V source, V target, boolean spath)
+ {
+ if(!g.containsVertex(source))
+ throw new IllegalArgumentException("Specified source vertex " +
+ source + " is not part of graph " + g);
+
+ if(!g.containsVertex(target))
+ throw new IllegalArgumentException("Specified target vertex " +
+ target + " is not part of graph " + g);
+
+ LinkedList<E> path = new LinkedList<E>();
+
+ // collect path data; must use internal method rather than
+ // calling getIncomingEdge() because getIncomingEdge() may
+ // wipe out results if results are not cached
+ Set<V> targets = new HashSet<V>();
+ targets.add(target);
+ if (spath == true) {
+ singleSourceShortestPath(source, targets, g.getVertexCount());
+ } else {
+ singleSourceMaxThroughputPath(source, targets, g.getVertexCount());
+ }
+ Map<V,E> incomingEdges =
+ ((SourcePathData)sourceMap.get(source)).incomingEdges;
+
+ if (incomingEdges.isEmpty() || incomingEdges.get(target) == null)
+ return path;
+ V current = target;
+ while (!current.equals(source))
+ {
+ E incoming = incomingEdges.get(current);
+ path.addFirst(incoming);
+ current = ((Graph<V,E>)g).getOpposite(current, incoming);
+ }
+ return path;
+ }
+
+ /**
+ * Returns a <code>List</code> of the edges on the shortest path from
+ * <code>source</code> to <code>target</code>, in order of their
+ * occurrence on this path.
+ * If either vertex is not in the graph for which this instance
+ * was created, throws <code>IllegalArgumentException</code>.
+ */
+ public List<E> getPath(V source, V target)
+ {
+
+ return getPath(source,target, true);
+ }
+
+ /**
+ * Returns a <code>List</code> of the edges on the Max Througput Shortest
+ * path from <code>source</code> to <code>target</code>, in order of their
+ * their occurrence on this path.
+ * Important - Transformer fn should return the appropriate edge weight
+ * for this API to return the Path Correctly.
+ * If either vertex is not in the graph for which this instance
+ * was created, throws <code>IllegalArgumentException</code>.
+ */
+ public List<E> getMaxThroughputPath(V source, V target)
+ {
+
+ return getPath(source,target, false);
+ }
+
+
+ /**
+ * <p>Returns a <code>LinkedHashMap</code> which maps each of the closest
+ * <code>numDist</code> vertices to the <code>source</code> vertex
+ * in the graph (including the <code>source</code> vertex)
+ * to the incoming edge along the path from that vertex. Throws
+ * an <code>IllegalArgumentException</code> if <code>source</code>
+ * is not in this instance's graph, or if <code>numDests</code> is
+ * either less than 1 or greater than the number of vertices in the
+ * graph.
+ *
+ * @see #getIncomingEdgeMap(Object)
+ * @see #getPath(Object,Object)
+ * @param source the vertex from which distances are measured
+ * @param numDests the number of vertices for which to measure distances
+ */
+ public LinkedHashMap<V,E> getIncomingEdgeMap(V source, int numDests)
+ {
+ if (g.getVertices().contains(source) == false)
+ throw new IllegalArgumentException("Specified source vertex " +
+ source + " is not part of graph " + g);
+
+ if (numDests < 1 || numDests > g.getVertexCount())
+ throw new IllegalArgumentException("numDests must be >= 1 " +
+ "and <= g.numVertices()");
+
+ singleSourceShortestPath(source, null, numDests);
+
+ LinkedHashMap<V,E> incomingEdgeMap =
+ ((SourcePathData)sourceMap.get(source)).incomingEdges;
+
+ if (!cached)
+ reset(source);
+
+ return incomingEdgeMap;
+ }
+
+
+ /**
+ * For a given source vertex, holds the estimated and final distances,
+ * tentative and final assignments of incoming edges on the shortest path from
+ * the source vertex, and a priority queue (ordered by estimaed distance)
+ * of the vertices for which distances are unknown.
+ *
+ * @author Joshua O'Madadhain
+ */
+ protected class SourcePathData extends SourceData
+ {
+ protected Map<V,E> tentativeIncomingEdges;
+ protected LinkedHashMap<V,E> incomingEdges;
+
+ protected SourcePathData(V source)
+ {
+ super(source);
+ incomingEdges = new LinkedHashMap<V,E>();
+ tentativeIncomingEdges = new HashMap<V,E>();
+ }
+
+ @Override
+ public void update(V dest, E tentative_edge, double new_dist)
+ {
+ super.update(dest, tentative_edge, new_dist);
+ tentativeIncomingEdges.put(dest, tentative_edge);
+ }
+
+ @Override
+ public Map.Entry<V,Number> getNextVertex()
+ {
+ Map.Entry<V,Number> p = super.getNextVertex();
+ V v = p.getKey();
+ E incoming = tentativeIncomingEdges.remove(v);
+ incomingEdges.put(v, incoming);
+ return p;
+ }
+
+ @Override
+ public void restoreVertex(V v, double dist)
+ {
+ super.restoreVertex(v, dist);
+ E incoming = incomingEdges.get(v);
+ tentativeIncomingEdges.put(v, incoming);
+ }
+
+ @Override
+ public void createRecord(V w, E e, double new_dist)
+ {
+ super.createRecord(w, e, new_dist);
+ tentativeIncomingEdges.put(w, e);
+ }
+
+ }
+
+}