package com.jedics.graph;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Hashtable;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import com.jedics.core.Handler;
import com.jedics.core.JediManager;
import com.jedics.graph.panels.GraphInformationPanel;
import com.jedics.graph.visual.GraphAnimator;
import com.jedics.graph.visual.GraphCanvas;
import com.jedics.graph.visual.VertexMouseHandler;
import com.jedics.graph.visual.event.VertexSelectionListener;
import com.jedics.swing.InformationPanel;
import com.jedics.util.SynchronizedList;
/**
* Abstract Top level controller used to create and show animations using the
* GraphCanvas class, and implementation of edges and vertexes.
*
* @author Abhishek Shroff
*
*/
public class GraphHandler extends Handler {
/**
* The heart of the I/O.
*/
protected GraphCanvas canvas;
protected VertexMouseHandler vmh;
protected GraphAnimator animator;
private SynchronizedList<VertexSelectionListener> selectionListeners;
private GraphInformationPanel graphInformationPanel;
/**
* List of all the vertexes.
*/
public final Hashtable<Integer, Vertex> vertices = new Hashtable<Integer, Vertex>();
/**
* Edge list... kinda
*/
public final Hashtable<Integer, Edge> edges = new Hashtable<Integer, Edge>();
/**
* counters for vertexes and edges so that they have unique identifiers.
* Unless of course we create more than 4294967295 of them.
*/
int vertexCounter, edgeCounter;
private boolean directed;
public GraphHandler(JediManager manager) {
super(manager);
}
public void setup() {
vertexCounter = 0;
edgeCounter = 0;
directed = false;
selectionListeners = new SynchronizedList<VertexSelectionListener>();
canvas = new GraphCanvas(this);
animator = new GraphAnimator(canvas, manager);
vmh = new VertexMouseHandler(this, canvas);
graphInformationPanel = new GraphInformationPanel(this, manager);
animator.setEdgeWeightVisible(true);
animator.setEdgeFlowVisible(false);
}
public void clear() {
canvas.clear();
vertices.clear();
edges.clear();
vertexCounter = 0;
edgeCounter = 0;
fireCanvasSelected();
}
public void reset() {
canvas.reset();
}
/**
* Add a <code>Vertex</code> to the Canvas. It also registers it with the
* handlers and adds it to the vertex list.
*
* @param v
* <code>Vertex</code> to add
*/
public Vertex createVertex() {
return createVertex(vertexCounter);
}
public Vertex createVertex(int id) {
while(vertices.containsKey(id)) id++;
vertexCounter = Math.max(vertexCounter, id+1);
Vertex v = new Vertex(id);
vertices.put(v.getId(), v);
fireVertexSelected(v.getId());
return v;
}
/**
* Remove the <code>Vertex</code> from the graph.
*
* @param v
* <code>Vertex</code> to remove.
*/
public void removeVertex(int id) {
Vertex v = vertices.remove(id);
for(Edge e : v.inbound()) {
e.getStart().outbound().remove(e);
edges.remove(e);
canvas.removeEdge(e.getId());
}
for(Edge e : v.outbound()) {
e.getEnd().inbound().remove(e);
edges.remove(e);
canvas.removeEdge(e.getId());
}
canvas.removeVertex(id);
}
public synchronized void addSelectionListener(VertexSelectionListener listener) {
selectionListeners.add(listener);
}
public synchronized void removeSelectionListener(VertexSelectionListener listener) {
selectionListeners.remove(listener);
}
public void fireVertexSelected(final int id) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Object[] listeners = selectionListeners.getList();
for (int i = 0; i< listeners.length; i++) {
((VertexSelectionListener)listeners[i]).vertexSelected(id);
}
}
});
}
public void fireEdgeSelected(final int id) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Object[] listeners = selectionListeners.getList();
for (int i = 0; i< listeners.length; i++) {
((VertexSelectionListener)listeners[i]).edgeSelected(id);
}
}
});
}
/**
* Add an <code>Edge</code> to the graph between the two given
* <code>Vertex</code>es.
*
* @param u Start vertex of the <code>Edge</code>
* @param v End vertex of the <code>Edge</code>
*/
public Edge createEdge(int u, int v) {
return createEdge(edgeCounter, u, v);
}
public Edge createEdge(int id, int u, int v) {
Vertex v1 = vertices.get(u);
Vertex v2 = vertices.get(v);
if (v1 == v2)
return null;
if (v1.containsEdgeTo(v2))
return null;
while(edges.containsKey(id)) id++;
edgeCounter = Math.max(edgeCounter, id+1);
Edge e = new Edge(v1, v2, id);
edges.put(e.getId(), e);
e.setDirected(directed);
fireEdgeSelected(e.getId());
return e;
}
public void removeEdge(int id) {
edges.remove(id);
canvas.removeEdge(id);
}
@Override
public synchronized void save(File file) {
try {
Vertex v;
Edge e;
PrintStream out = new PrintStream(new FileOutputStream(file, false));
out.printf("G,%s,%s,%s\n", isDirected(), animator.isEdgeWeightVisible(), animator.isEdgeFlowVisible());
for(Integer i : vertices.keySet()) {
v = vertices.get(i);
out.printf("V,%d,%s,%s,%s,%d,%d\n", v.getId(), animator.getData(v.getId()), animator.getEastText(v.getId()), animator.getSouthText(v.getId()), animator.getX(v.getId()), animator.getY(v.getId()));
}
for(Integer i : edges.keySet()) {
e = edges.get(i);
out.printf("E,%s,%d,%d,%d,%f,%f,%f\n", e.getClass().getName(),e.getId(), e.getStart().getId(), e.getEnd().getId(), e.getWeight(), e.getFlow(), e.getCapacity());
}
out.close();
} catch(FileNotFoundException ex) {
JOptionPane.showMessageDialog(canvas, "You do not have the necessary permissions to write to this file.", "Unable to Save", JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void load(File file) {
try {
Edge e;
Vertex v;
BufferedReader in = new BufferedReader(new FileReader(file));
String inStr = in.readLine();
String[] data;
if(inStr == null) return;
data = inStr.split(",");
if(!data[0].equals("G")) return;
clear();
setDirected(Boolean.parseBoolean(data[1]));
animator.setEdgeWeightVisible(Boolean.parseBoolean(data[2]));
animator.setEdgeFlowVisible(Boolean.parseBoolean(data[3]));
while((inStr = in.readLine()) != null) {
data = inStr.split(",");
if(!data[0].equals("V")) break;
v = createVertex(Integer.parseInt(data[1]));
canvas.addVertex(v, Integer.parseInt(data[5]), Integer.parseInt(data[6]));
animator.setData(v.getId(), data[2]);
animator.setEastText(v.getId(), data[3]);
animator.setSouthText(v.getId(), data[4]);
}
while(inStr != null) {
data = inStr.split(",");
if(!data[0].equals("E")) break;
if(data[1].indexOf("Line") != -1) {
e = createEdge(Integer.parseInt(data[2]), Integer.parseInt(data[3]), Integer.parseInt(data[4]));
canvas.addEdge(e);
} else {
inStr = in.readLine();
continue;
}
e.setWeight(Float.parseFloat(data[5]));
e.setCapacity(Float.parseFloat(data[6]));
e.setFlow(Float.parseFloat(data[7]));
edgeCounter = Math.max(edgeCounter, e.getId()+1);
inStr = in.readLine();
}
in.close();
reset();
} catch(FileNotFoundException ex) {
JOptionPane.showMessageDialog(canvas, "You do not have the necessary permissions to read this file.", "Unable to Load", JOptionPane.ERROR_MESSAGE);
} catch(IOException ex) {
String err = "An unknown error occured while loading this file. Details can be found in the error log.\n\nPlease send me a copy of your error log for review so that I can fix it.";
JOptionPane.showMessageDialog(canvas, err, "Unable to Load", JOptionPane.ERROR_MESSAGE);
System.err.println("Could not write to the file. Unable to save.");
} catch(Exception ex) {
JOptionPane.showMessageDialog(canvas, "The specified file " + file.getAbsolutePath() + " is not in the correct format.", "Invalid File format", JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
}
}
/**
* Is the Graph directed?
*
* @return true if the graph is directed. False otherwise.
*/
public boolean isDirected() {
return directed;
}
public void setDirected(boolean directed) {
if(directed == this.directed) return;
this.directed = directed;
for (Integer i : edges.keySet()) {
edges.get(i).setDirected(directed);
}
canvas.repaint();
}
public int getVertexCount() {
return vertices.size();
}
public int getEdgeCount() {
return edges.size();
}
public GraphCanvas getCanvas() {
return canvas;
}
public void repaintCanvas() {
canvas.repaint();
}
public GraphAnimator getAnimator() {
return animator;
}
@Override
public InformationPanel getInformationPanel() {
return graphInformationPanel;
}
}