/*
* Copyright (c) 2001-2005, Gaudenz Alder
*
* All rights reserved.
*
* This file is licensed under the JGraph software license, a copy of which
* will have been provided to you in the file LICENSE at the root of your
* installation directory. If you are unable to locate this file please
* contact JGraph sales for another copy.
*/
package com.mxgraph.analysis;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
/**
* A singleton class that provides algorithms for graphs. Assume these
* variables for the following examples:<br>
* <code>
* mxICostFunction cf = mxDistanceCostFunction();
* Object[] v = graph.getChildVertices(graph.getDefaultParent());
* Object[] e = graph.getChildEdges(graph.getDefaultParent());
* mxGraphAnalysis mga = mxGraphAnalysis.getInstance();
* </code>
*
* <h3>Shortest Path (Dijkstra)</h3>
*
* For example, to find the shortest path between the first and the second
* selected cell in a graph use the following code: <br>
* <br>
* <code>Object[] path = mga.getShortestPath(graph, from, to, cf, v.length, true);</code>
*
* <h3>Minimum Spanning Tree</h3>
*
* This algorithm finds the set of edges with the minimal length that connect
* all vertices. This algorithm can be used as follows:
* <h5>Prim</h5>
* <code>mga.getMinimumSpanningTree(graph, v, cf, true))</code>
* <h5>Kruskal</h5>
* <code>mga.getMinimumSpanningTree(graph, v, e, cf))</code>
*
* <h3>Connection Components</h3>
*
* The union find may be used as follows to determine whether two cells are
* connected: <code>boolean connected = uf.differ(vertex1, vertex2)</code>.
*
* @see mxICostFunction
*/
public class mxGraphAnalysis
{
/**
* Holds the shared instance of this class.
*/
protected static mxGraphAnalysis instance = new mxGraphAnalysis();
/**
*
*/
protected mxGraphAnalysis()
{
// empty
}
/**
* @return Returns the sharedInstance.
*/
public static mxGraphAnalysis getInstance()
{
return instance;
}
/**
* Sets the shared instance of this class.
*
* @param instance The instance to set.
*/
public static void setInstance(mxGraphAnalysis instance)
{
mxGraphAnalysis.instance = instance;
}
/**
* Returns the shortest path between two cells or their descendants
* represented as an array of edges in order of traversal. <br>
* This implementation is based on the Dijkstra algorithm.
*
* @param graph The object that defines the graph structure
* @param from The source cell.
* @param to The target cell (aka sink).
* @param cf The cost function that defines the edge length.
* @param steps The maximum number of edges to traverse.
* @param directed If edge directions should be taken into account.
* @return Returns the shortest path as an alternating array of vertices
* and edges, starting with <code>from</code> and ending with
* <code>to</code>.
*
* @see #createPriorityQueue()
*/
public Object[] getShortestPath(mxGraph graph, Object from, Object to,
mxICostFunction cf, int steps, boolean directed)
{
// Sets up a pqueue and a hashtable to store the predecessor for each
// cell in tha graph traversal. The pqueue is initialized
// with the from element at prio 0.
mxGraphView view = graph.getView();
mxFibonacciHeap q = createPriorityQueue();
Hashtable<Object, Object> pred = new Hashtable<Object, Object>();
q.decreaseKey(q.getNode(from, true), 0); // Inserts automatically
// The main loop of the dijkstra algorithm is based on the pqueue being
// updated with the actual shortest distance to the source vertex.
for (int j = 0; j < steps; j++)
{
mxFibonacciHeap.Node node = q.removeMin();
double prio = node.getKey();
Object obj = node.getUserObject();
// Exits the loop if the target node or vertex has been reached
if (obj == to)
{
break;
}
// Gets all outgoing edges of the closest cell to the source
Object[] e = (directed) ? graph.getOutgoingEdges(obj) : graph
.getConnections(obj);
if (e != null)
{
for (int i = 0; i < e.length; i++)
{
Object[] opp = graph.getOpposites(new Object[] { e[i] },
obj);
if (opp != null && opp.length > 0)
{
Object neighbour = opp[0];
// Updates the priority in the pqueue for the opposite node
// to be the distance of this step plus the cost to
// traverese the edge to the neighbour. Note that the
// priority queue will make sure that in the next step the
// node with the smallest prio will be traversed.
if (neighbour != null && neighbour != obj
&& neighbour != from)
{
double newPrio = prio
+ ((cf != null) ? cf.getCost(view
.getState(e[i])) : 1);
node = q.getNode(neighbour, true);
double oldPrio = node.getKey();
if (newPrio < oldPrio)
{
pred.put(neighbour, e[i]);
q.decreaseKey(node, newPrio);
}
}
}
}
}
if (q.isEmpty())
{
break;
}
}
// Constructs a path array by walking backwards through the predessecor
// map and filling up a list of edges, which is subsequently returned.
ArrayList<Object> list = new ArrayList<Object>(2 * steps);
Object obj = to;
Object edge = pred.get(obj);
if (edge != null)
{
list.add(obj);
while (edge != null)
{
list.add(0, edge);
mxCellState state = view.getState(edge);
Object source = (state != null) ? state
.getVisibleTerminal(true) : view.getVisibleTerminal(
edge, true);
boolean isSource = source == obj;
obj = (state != null) ? state.getVisibleTerminal(!isSource)
: view.getVisibleTerminal(edge, !isSource);
list.add(0, obj);
edge = pred.get(obj);
}
}
return list.toArray();
}
/**
* Returns the minimum spanning tree (MST) for the graph defined by G=(E,V).
* The MST is defined as the set of all vertices with minimal lengths that
* forms no cycles in G.<br>
* This implementation is based on the algorihm by Prim-Jarnik. It uses
* O(|E|+|V|log|V|) time when used with a Fibonacci heap and a graph whith a
* double linked-list datastructure, as is the case with the default
* implementation.
*
* @param graph
* the object that describes the graph
* @param v
* the vertices of the graph
* @param cf
* the cost function that defines the edge length
*
* @return Returns the MST as an array of edges
*
* @see #createPriorityQueue()
*/
public Object[] getMinimumSpanningTree(mxGraph graph, Object[] v,
mxICostFunction cf, boolean directed)
{
ArrayList<Object> mst = new ArrayList<Object>(v.length);
// Sets up a pqueue and a hashtable to store the predecessor for each
// cell in tha graph traversal. The pqueue is initialized
// with the from element at prio 0.
mxFibonacciHeap q = createPriorityQueue();
Hashtable<Object, Object> pred = new Hashtable<Object, Object>();
Object u = v[0];
q.decreaseKey(q.getNode(u, true), 0);
for (int i = 1; i < v.length; i++)
{
q.getNode(v[i], true);
}
// The main loop of the dijkstra algorithm is based on the pqueue being
// updated with the actual shortest distance to the source vertex.
while (!q.isEmpty())
{
mxFibonacciHeap.Node node = q.removeMin();
u = node.getUserObject();
Object edge = pred.get(u);
if (edge != null)
{
mst.add(edge);
}
// Gets all outgoing edges of the closest cell to the source
Object[] e = (directed) ? graph.getOutgoingEdges(u) : graph
.getConnections(u);
Object[] opp = graph.getOpposites(e, u);
if (e != null)
{
for (int i = 0; i < e.length; i++)
{
Object neighbour = opp[i];
// Updates the priority in the pqueue for the opposite node
// to be the distance of this step plus the cost to
// traverese the edge to the neighbour. Note that the
// priority queue will make sure that in the next step the
// node with the smallest prio will be traversed.
if (neighbour != null && neighbour != u)
{
node = q.getNode(neighbour, false);
if (node != null)
{
double newPrio = cf.getCost(graph.getView()
.getState(e[i]));
double oldPrio = node.getKey();
if (newPrio < oldPrio)
{
pred.put(neighbour, e[i]);
q.decreaseKey(node, newPrio);
}
}
}
}
}
}
return mst.toArray();
}
/**
* Returns the minimum spanning tree (MST) for the graph defined by G=(E,V).
* The MST is defined as the set of all vertices with minimal lenths that
* forms no cycles in G.<br>
* This implementation is based on the algorihm by Kruskal. It uses
* O(|E|log|E|)=O(|E|log|V|) time for sorting the edges, O(|V|) create sets,
* O(|E|) find and O(|V|) union calls on the union find structure, thus
* yielding no more than O(|E|log|V|) steps. For a faster implementatin
*
* @see #getMinimumSpanningTree(mxGraph, Object[], mxICostFunction,
* boolean)
*
* @param graph The object that contains the graph.
* @param v The vertices of the graph.
* @param e The edges of the graph.
* @param cf The cost function that defines the edge length.
*
* @return Returns the MST as an array of edges.
*
* @see #createUnionFind(Object[])
*/
public Object[] getMinimumSpanningTree(mxGraph graph, Object[] v,
Object[] e, mxICostFunction cf)
{
// Sorts all edges according to their lengths, then creates a union
// find structure for all vertices. Then walks through all edges by
// increasing length and tries adding to the MST. Only edges are added
// that do not form cycles in the graph, that is, where the source
// and target are in different sets in the union find structure.
// Whenever an edge is added to the MST, the two different sets are
// unified.
mxGraphView view = graph.getView();
mxUnionFind uf = createUnionFind(v);
ArrayList<Object> result = new ArrayList<Object>(e.length);
mxCellState[] edgeStates = sort(view.getCellStates(e), cf);
for (int i = 0; i < edgeStates.length; i++)
{
Object source = edgeStates[i].getVisibleTerminal(true);
Object target = edgeStates[i].getVisibleTerminal(false);
mxUnionFind.Node setA = uf.find(uf.getNode(source));
mxUnionFind.Node setB = uf.find(uf.getNode(target));
if (setA == null || setB == null || setA != setB)
{
uf.union(setA, setB);
result.add(edgeStates[i].getCell());
}
}
return result.toArray();
}
/**
* Returns a union find structure representing the connection components of
* G=(E,V).
*
* @param graph The object that contains the graph.
* @param v The vertices of the graph.
* @param e The edges of the graph.
* @return Returns the connection components in G=(E,V)
*
* @see #createUnionFind(Object[])
*/
public mxUnionFind getConnectionComponents(mxGraph graph, Object[] v,
Object[] e)
{
mxGraphView view = graph.getView();
mxUnionFind uf = createUnionFind(v);
for (int i = 0; i < e.length; i++)
{
mxCellState state = view.getState(e[i]);
Object source = (state != null) ? state.getVisibleTerminal(true)
: view.getVisibleTerminal(e[i], true);
Object target = (state != null) ? state.getVisibleTerminal(false)
: view.getVisibleTerminal(e[i], false);
uf.union(uf.find(uf.getNode(source)), uf.find(uf.getNode(target)));
}
return uf;
}
/**
* Returns a sorted set for <code>cells</code> with respect to
* <code>cf</code>.
*
* @param states
* the cell states to sort
* @param cf
* the cost function that defines the order
*
* @return Returns an ordered set of <code>cells</code> wrt.
* <code>cf</code>
*/
public mxCellState[] sort(mxCellState[] states, final mxICostFunction cf)
{
List<mxCellState> result = Arrays.asList(states);
Collections.sort(result, new Comparator<mxCellState>()
{
/**
*
*/
public int compare(mxCellState o1, mxCellState o2)
{
Double d1 = new Double(cf.getCost(o1));
Double d2 = new Double(cf.getCost(o2));
return d1.compareTo(d2);
}
});
return (mxCellState[]) result.toArray();
}
/**
* Returns the sum of all cost for <code>cells</code> with respect to
* <code>cf</code>.
*
* @param states
* the cell states to use for the sum
* @param cf
* the cost function that defines the costs
*
* @return Returns the sum of all cell cost
*/
public double sum(mxCellState[] states, mxICostFunction cf)
{
double sum = 0;
for (int i = 0; i < states.length; i++)
{
sum += cf.getCost(states[i]);
}
return sum;
}
/**
* Hook for subclassers to provide a custom union find structure.
*
* @param v
* the array of all elements
*
* @return Returns a union find structure for <code>v</code>
*/
protected mxUnionFind createUnionFind(Object[] v)
{
return new mxUnionFind(v);
}
/**
* Hook for subclassers to provide a custom fibonacci heap.
*/
protected mxFibonacciHeap createPriorityQueue()
{
return new mxFibonacciHeap();
}
}