package doser.sequencedetection.graph;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
/**
* Class of a concrete implementation of the standard Dijkstra algorithm for
* Node<T> Object.
*/
public class Dijkstra<T extends Comparable<T>> {
private AbstractGraph<T> graph;
/**
* The heap used by the dijkstra algorithm
*/
private PriorityQueue<Node<T>> heap;
/**
* Constructs a Dijkstra object.
*/
public Dijkstra(AbstractGraph<T> graph) {
this.graph = graph;
}
/**
* Searches for all neighbours of the the node in the graph and puts them
* into the heap. Further the distance to source and the nodetype are
* updated
*
* @param current
* the node of current calculation
*/
private void addNeighboursToHeap(Node<T> current) {
DijkstraData<T> currentNodeData = getData(current);
for (Edge<T> edge : current.getOutgoingEdges()) {
// Edge must be visible
if (!edge.getVisibleStatus()) {
Node<T> neighbour = edge.getEndNode();
if (getData(neighbour) == null) {
neighbour.setDijkstraData(new DijkstraData<T>());
}
DijkstraData<T> neighbourNodeData = getData(neighbour);
if (!isBlack(neighbour)) {
double newDist = currentNodeData.getDistanceToSource()
+ edge.getDijkstraWeight();
if (newDist < neighbourNodeData.getDistanceToSource()) {
neighbourNodeData.setDistanceToSource(newDist);
neighbourNodeData.setPredecessor(current);
if (newDist < Double.MAX_VALUE
&& neighbourNodeData.getNodeType() == NodeTypes.WHITE) {
heap.add(neighbour);
neighbourNodeData.setNodeType(NodeTypes.GREY);
}
}
}
}
}
}
/**
* Builds the calculated shortest route (in respect to the RoutePlaner).
*
* @param source
* the source of the route
* @param target
* the target of the route
* @return the calculated shortest route (in respect to the RoutePlaner)
*/
private List<T> buildRoute(Node<T> source, Node<T> target) {
LinkedList<Node<T>> route = new LinkedList<Node<T>>();
Node<T> current = target;
while (current != source) {
DijkstraData<T> data = getData(current);
route.addFirst(current);
current = data.getPredecessor();
}
route.addFirst(source);
return extractNodeData(route);
}
public List<T> calculateRoute(Node<T> startNode, Node<T> endNode)
throws NoRouteFoundException {
if (startNode == null || endNode == null) {
throw new NoRouteFoundException();
}
heap = new PriorityQueue<Node<T>>(1, new Comparator<Node<T>>() {
@Override
public int compare(Node<T> o1, Node<T> o2) {
double length1 = getData(o1).getDistanceToSource();
double length2 = getData(o2).getDistanceToSource();
return (int) Math.signum(length1 - length2);
}
});
graph.resetDijkstraData();
startNode.setDijkstraData(new DijkstraData<T>());
DijkstraData<T> data = getData(startNode);
data.setDistanceToSource(0.0);
data.setNodeType(NodeTypes.GREY);
heap.add(startNode);
Node<T> current;
do {
current = heap.poll();
setBlack(current);
addNeighboursToHeap(current);
} while (current != endNode && heap.size() != 0);
// No route found
if (current != endNode) {
throw new NoRouteFoundException();
} else {
return buildRoute(startNode, endNode);
}
}
private List<T> extractNodeData(List<Node<T>> nodeLst) {
List<T> result = new LinkedList<T>();
for (Node<T> node2 : nodeLst) {
Node<T> node = node2;
T t = node.getData();
result.add(t);
}
return result;
}
/**
* Returns the DijkstraData object of the node
*
* @param node
*
* @return the DijkstraData object of the node
*/
private DijkstraData<T> getData(Node<T> node) {
return node.getDijkstraData();
}
/**
* Cecks if the node is set black.
*
* @param node
* the node that is checked
* @return true if the node is black, else false
*/
private boolean isBlack(Node<T> node) {
DijkstraData<T> data = node.getDijkstraData();
if (data == null) {
return false;
}
if (data.getNodeType() == NodeTypes.BLACK) {
return true;
}
return false;
}
/**
* Sets the current node black.
*
* @param node
* the node which is set black
*/
private void setBlack(Node<T> node) {
if (node.getDijkstraData() == null) {
node.setDijkstraData(new DijkstraData<T>());
}
DijkstraData<T> data = node.getDijkstraData();
data.setNodeType(NodeTypes.BLACK);
}
}