/*
* (C) Copyright IBM Corp. 2009
*
* LICENSE: Eclipse Public License v1.0
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.ibm.gaiandb.apps.dashboard;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.Action;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.ShapeAction;
import prefuse.action.assignment.SizeAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.Control;
import prefuse.controls.ControlAdapter;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.render.RendererFactory;
import prefuse.render.ShapeRenderer;
import prefuse.util.GraphicsLib;
import prefuse.util.display.DisplayLib;
import prefuse.util.force.DragForce;
import prefuse.util.force.ForceSimulator;
import prefuse.util.force.NBodyForce;
import prefuse.util.force.SpringForce;
import prefuse.visual.EdgeItem;
import prefuse.visual.VisualItem;
import com.ibm.gaiandb.tools.NetworkLinksAnalyser;
public class LtAndDsGraph extends Display {
// Use PROPRIETARY notice if class contains a main() method, otherwise use
// COPYRIGHT notice.
public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2011";
private static final long serialVersionUID = 0L;
private static final String GROUP = "data";
private static final String NODE_GROUP = GROUP + ".nodes";
private static final String EDGE_GROUP = GROUP + ".edges";
private static final String NODE_ID_FIELD = "node";
private static final String SOURCE_ID_FIELD = "source";
private static final String TARGET_ID_FIELD = "target";
private static final String NODE_NAME_FIELD = "name";
private static final Color TEXT_COLOR = Color.WHITE;
private static final Color DEFAULT_NODE_COLOR = Color.BLACK;
private static final Color DEFAULT_LT_COLOR = Color.LIGHT_GRAY;
private static final Color EDGE_COLOR = Color.LIGHT_GRAY;
private static final Color SELECTED_EDGE_COLOR = Color.BLACK;
private static final int EDGE_THICKNESS = 1;
private static final int SPRING_LENGTH_MULTIPLIER = 100;
NetworkLinksAnalyser nla = new NetworkLinksAnalyser();
public static class Node implements Comparable<Node> {
private static final Map<String, Node> INSTANCES = new HashMap<String, Node>();
private final String name;
private Color color = null;
private Node(String name) {
this.name = name;
}
public static synchronized Node getInstance(String name) {
Node instance = INSTANCES.get(name);
if (null == instance) {
instance = new Node(name);
INSTANCES.put(name, instance);
}
return instance;
}
public static Set<String> getAllNodeNames() {
return INSTANCES.keySet();
}
public static void removeInstance(String name) {
INSTANCES.remove(name);
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Node)) {
return false;
}
return name.equals(((Node) o).name);
}
public int hashCode() {
return name.hashCode();
}
public int compareTo(Node o) {
return name.compareToIgnoreCase(o.name);
}
public String toString() {
return name;
}
}
public static class Edge implements Comparable<Edge> {
public final Node source;
public final Node target;
public Edge(String sourceName, String targetName) {
Node source = Node.getInstance(sourceName);
Node target = Node.getInstance(targetName);
int comparison = source.compareTo(target);
if (comparison <= 0) {
this.source = source;
this.target = target;
} else {
this.source = target;
this.target = source;
}
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Edge)) {
return false;
}
Edge other = (Edge) o;
return (source.equals(other.source) && target.equals(other.target));
}
public int hashCode() {
return source.hashCode() ^ target.hashCode();
}
public int compareTo(Edge o) {
int ret;
ret = source.compareTo(o.source);
if (0 != ret) {
return ret;
}
ret = target.compareTo(o.target);
if (0 != ret) {
return ret;
}
return 0;
}
public String toString() {
return source.name + " <-> " + target.name;
}
}
private static LtAndDsTab parentTab = null;
public Graph data;
public Table nodeTable;
public Table edgeTable;
private Map<Node, Integer> nodes = new TreeMap<Node, Integer>();
private Map<Edge, Integer> edges = new TreeMap<Edge, Integer>();
private Map<Node, Color> newNodeColors = new TreeMap<Node, Color>();
private Set<Edge> newEdges = new TreeSet<Edge>();
private MonitorInfo monitor = null;
// private JPopupMenu popup = new JPopupMenu("POPUP!");
private String localNode = null;
private String currentHoverItemName = null;
private String lastClickedNode = null;
private RendererFactory rendererWithoutLabels;
private RendererFactory rendererWithLabels;
private boolean isBackgroundSelected = false;
public synchronized void recenter() {
if (null == m_vis || isTranformInProgress()
|| null != currentHoverItemName || isBackgroundSelected)
return;
try {
long m_duration = 500;
int m_margin = 50;
Rectangle2D bounds = m_vis.getBounds(Visualization.ALL_ITEMS);
// System.out.println(
// "Display size: width: " + this.getWidth() + ", height: " +
// this.getHeight()
// "\nVis bounds: center: " + bounds.getCenterX() + ", " +
// bounds.getCenterY() +
// ", bounds X: " + bounds.getMinX() + ", " + bounds.getMaxX() +
// ", bounds Y: " + bounds.getMinY() + ", " + bounds.getMaxY()
// );
if (bounds.getMinX() == bounds.getMaxX()
|| bounds.getMinY() == bounds.getMaxY())
return;
GraphicsLib.expand(bounds, m_margin + (int) (1 / getScale()));
DisplayLib.fitViewToBounds(this, bounds, m_duration);
} catch (Exception e) {
System.out.println("Exception in recenter(): " + e);
}
}
private SpringForce springForce = new SpringForce(1e-5f,
SpringForce.DEFAULT_SPRING_LENGTH);
public MonitorInfo getMonitor() {
return monitor;
}
public void setMonitor(MonitorInfo monitor) {
this.monitor = monitor;
}
public int getNodeCount() {
return null != data ? data.getNodeCount() : 0;
}
public String getLocalNode() {
return localNode;
}
public void setLocalNode(String localNode) {
this.localNode = localNode;
}
public String getCurrentItemName() {
return currentHoverItemName;
}
public String getAndUnsetLastClickedNode() {
String lcn = lastClickedNode;
System.out.println("clicked " + lcn);
lastClickedNode = null;
return lcn;
}
public void setNodeRenderer(boolean withLabels) {
if (withLabels) {
m_vis.setRendererFactory(rendererWithLabels);
} else {
m_vis.setRendererFactory(rendererWithoutLabels);
}
}
private static final Object lock = new Object();
private static LtAndDsGraph graph = null;
public static LtAndDsGraph getSingleton(LtAndDsTab context) {
synchronized (lock) {
if (null == graph)
graph = new LtAndDsGraph();
}
parentTab = context;
return graph;
}
private LtAndDsGraph() {
super(new Visualization());
String[] nodeColumns = { NODE_ID_FIELD, NODE_NAME_FIELD };
Class<?>[] nodeColumnTypes = { int.class, String.class };
nodeTable = new Schema(nodeColumns, nodeColumnTypes).instantiate();
data = new Graph(nodeTable, false, NODE_ID_FIELD, SOURCE_ID_FIELD,
TARGET_ID_FIELD);
edgeTable = data.getEdgeTable();
m_vis.addGraph(GROUP, data);
ShapeRenderer nodeRendererWithoutLabels = new ShapeRenderer();
LabelRenderer nodeRendererWithLabels = new LabelRenderer(
NODE_NAME_FIELD);
nodeRendererWithLabels.setRoundedCorner(10, 10);
EdgeRenderer edgeRenderer = new EdgeRenderer() {
public void render(Graphics2D g, VisualItem item) {
EdgeItem edgeItem = (EdgeItem) item;
if (null != currentHoverItemName
&& (currentHoverItemName.equals(edgeItem
.getSourceItem().getString(NODE_NAME_FIELD)) || currentHoverItemName
.equals(edgeItem.getTargetItem().getString(
NODE_NAME_FIELD)))) {
item.setSize(EDGE_THICKNESS * 2);
item.setStrokeColor(SELECTED_EDGE_COLOR.getRGB());
} else {
item.setSize(EDGE_THICKNESS);
item.setStrokeColor(EDGE_COLOR.getRGB());
}
super.render(g, item);
}
};
rendererWithoutLabels = new DefaultRendererFactory(
nodeRendererWithoutLabels, edgeRenderer);
rendererWithLabels = new DefaultRendererFactory(nodeRendererWithLabels,
edgeRenderer);
m_vis.setRendererFactory(rendererWithoutLabels);
// Set up the drawing actions.
ActionList draw = new ActionList();
draw.add(new ShapeAction(NODE_GROUP, Constants.SHAPE_ELLIPSE) {
public int getShape(VisualItem item) {
if (null != localNode
&& localNode.equals(item.getString(NODE_NAME_FIELD))) {
return Constants.SHAPE_STAR;
} else {
return super.getShape(item);
}
}
});
draw.add(new SizeAction(NODE_GROUP) {
public double getSize(VisualItem item) {
if (null != localNode
&& localNode.equals(item.getString(NODE_NAME_FIELD))) {
return super.getSize(item) * 2;
} else {
return super.getSize(item);
}
}
});
// draw.add(new ColorAction(NODE_GROUP, VisualItem.TEXTCOLOR,
// TEXT_COLOR.getRGB()));
draw.add(new ColorAction(EDGE_GROUP, VisualItem.STROKECOLOR, EDGE_COLOR
.getRGB()));
draw.add(new RepaintAction());
m_vis.putAction("draw", draw);
Action selectNode = new ColorAction(NODE_GROUP, VisualItem.STROKECOLOR,
DEFAULT_NODE_COLOR.getRGB()) {
public int getColor(VisualItem item) {
if (!item.isInGroup(Visualization.FOCUS_ITEMS))
return item.getFillColor();
return Color.CYAN.getRGB();
}
};
m_vis.putAction("selectNode", selectNode);
ActionList nodeColor = new ActionList();
nodeColor.add(new ColorAction(NODE_GROUP, VisualItem.FILLCOLOR,
Color.LIGHT_GRAY.getRGB()) {
public int getColor(VisualItem item) {
if (null == monitor)
return super.getColor(item);
String nodeName = item.getString(NODE_NAME_FIELD);
Color color = Node.getInstance(nodeName).getColor();
if (null == color)
return super.getColor(item);
return color.getRGB();
}
});
nodeColor.add(new ColorAction(NODE_GROUP, VisualItem.TEXTCOLOR,
TEXT_COLOR.getRGB()) {
public int getColor(VisualItem item) {
String nodeName = item.getString(NODE_NAME_FIELD);
Color color = Node.getInstance(nodeName).getColor();
if (null == color || !color.equals(Color.YELLOW))
return Color.WHITE.getRGB();
return Color.BLACK.getRGB();
}
});
nodeColor.add(selectNode);
m_vis.putAction("nodeColor", nodeColor);
// Set up the positioning actions.
ForceSimulator forces = new ForceSimulator();
forces.addForce(new NBodyForce());
forces.addForce(springForce);
forces.addForce(new DragForce());
ActionList position = new ActionList(Activity.INFINITY);
position.add(new ForceDirectedLayout(GROUP, forces, false));
position.add(new RepaintAction());
m_vis.putAction("position", position);
// for ( String s : new String[] { "Kill", "Execute Last Query" } ) {
// JMenuItem jm = new JMenuItem(s);
// // jm.addActionListener(this);
// popup.add(jm);
// }
// Set up movement and zooming for windows and nodes.
addControlListener(new DragControl()); // { public void
// mouseClicked(MouseEvent e) {
// graph.requestFocus();
// super.mouseClicked(e); } } );
addControlListener(new PanControl());
addControlListener(new ZoomControl());
addControlListener(new WheelZoomControl());
addControlListener(new FocusControl(1, "selectNode") {
public void mouseClicked(MouseEvent e) {
// System.out.println("button clicked: " + e.getButton() +
// ", LEFT=" + Control.LEFT_MOUSE_BUTTON +
// ", fo: " + graph.isFocusOwner() + ", fr: " +
// graph.isRequestFocusEnabled() + ", froot: " +
// graph.isFocusCycleRoot());
if (!graph.isFocusOwner())
graph.requestFocus();
else if (MouseEvent.BUTTON1 == e.getButton()
&& !e.isControlDown()) {
TupleSet ts = m_vis
.getFocusGroup(Visualization.FOCUS_ITEMS);
ts.clear();
curFocus = null;
m_vis.run(activity);
}
super.mouseClicked(e);
}
public void itemClicked(VisualItem item, MouseEvent e) {
String nodeName = item.getString(NODE_NAME_FIELD);
// If we are editing a cell, we do not want to update the table
// information,
// so we do nothing. Otherwise...
if (parentTab.filterText.isEnabled()) {
if (nodeName.equals(lastClickedNode)) {
lastClickedNode = null;
parentTab.savedFilter = null;
parentTab.filterDisplay(lastClickedNode);
} else {
lastClickedNode = item.getString(NODE_NAME_FIELD);
if (!lastClickedNode.equals(localNode)) {
parentTab.savedFilter = lastClickedNode;
parentTab.filterDisplay(lastClickedNode);
}
}
}
super.itemClicked(item, e);
if (!e.isControlDown()) {
TupleSet ts = m_vis
.getFocusGroup(Visualization.FOCUS_ITEMS);
ts.clear();
curFocus = null;
m_vis.run(activity);
}
}
});
addControlListener(new ZoomToFitControl(Control.MIDDLE_MOUSE_BUTTON));
addControlListener(new ControlAdapter() {
public void itemEntered(VisualItem item, MouseEvent event) {
if (item.isInGroup(NODE_GROUP)) {
currentHoverItemName = item.getString(NODE_NAME_FIELD);
}
}
public void itemExited(VisualItem item, MouseEvent event) {
currentHoverItemName = null;
}
public void mousePressed(MouseEvent event) {
isBackgroundSelected = true;
}
public void mouseReleased(MouseEvent event) {
isBackgroundSelected = false;
}
public void itemClicked(VisualItem item, MouseEvent event) {
if (event.isShiftDown() && item.isInGroup(NODE_GROUP)) { // 2 ==
// event.getClickCount()
// )
// {
// DRV - 22/10/2011 - commented out node clicking action.
// Security credentials are not to be entered this way
// anymore.
// this should be customer/policy-plugin specific now
// lastClickedNode = item.getString(NODE_NAME_FIELD);
return;
}
// if ( MouseEvent.BUTTON3 == event.getButton() ) {
// // System.out.println("comp: " + event.getComponent() +
// ", X: " + event.getX() + ", Y: " + event.getY() );
// // System.out.println("comp: " + event.getComponent() +
// ", X: " + event.getXOnScreen() + ", Y: " +
// event.getYOnScreen());
// popup.show(event.getComponent(), event.getX(), event.getY());
// }
}
public void keyTyped(KeyEvent event) {
if (event.isControlDown() && event.getKeyChar() == 1) { // ==
// Ctrl-A
// =>
// toggle
// select/unselect
// all
// nodes
TupleSet ts = m_vis
.getFocusGroup(Visualization.FOCUS_ITEMS);
int tupleCount = ts.getTupleCount();
ts.clear();
if (nodeTable.getRowCount() != tupleCount) {
@SuppressWarnings("unchecked")
Iterator<VisualItem> it = m_vis.getGroup(NODE_GROUP)
.tuples();
while (it.hasNext())
ts.addTuple(it.next());
}
m_vis.run("selectNode");
}
}
});
}
public Set<String> getSelectedNodes() {
TupleSet ts = (TupleSet) m_vis.getFocusGroup(Visualization.FOCUS_ITEMS);
Set<String> nodes = new HashSet<String>();
@SuppressWarnings("unchecked")
Iterator<VisualItem> it = ts.tuples();
try {
while (it.hasNext())
nodes.add(it.next().getString(NODE_NAME_FIELD));
} catch (Exception e) {
return null;
}
return nodes;
}
public synchronized void update() {
synchronized (newEdges) {
m_vis.cancel("position");
processNewEdges();
if (0 == data.getNodeCount()) {
return;
}
springForce
.setMaxValue(
SpringForce.SPRING_LENGTH,
(float) (Math.log10(data.getNodeCount()) * SPRING_LENGTH_MULTIPLIER));
m_vis.run("position");
m_vis.run("draw");
m_vis.run("nodeColor");
}
processNewNodeColors();
}
// public synchronized void updateValues() {
// processNewNodeColors();
// }
public void setNodeColor(String nodeName, Color color) {
if (null != color) {
synchronized (newNodeColors) {
newNodeColors.put(Node.getInstance(nodeName), color);
}
}
}
public void setEdge(String sourceName, String targetName) {
synchronized (newEdges) {
newEdges.add(new Edge(sourceName, targetName));
}
}
private void processNewNodeColors() {
for (Node node : nodes.keySet()) {
if (node.name.equals(localNode)) {
node.setColor(DEFAULT_NODE_COLOR);
} else {
node.setColor(DEFAULT_LT_COLOR);
}
}
for (Entry<Node, Color> nodeValue : newNodeColors.entrySet()) {
nodeValue.getKey().setColor(nodeValue.getValue());
}
newNodeColors.clear();
m_vis.run("nodeColor");
}
private void processNewEdges() {
boolean isGraphChanged = false;
Set<Node> newNodes = new HashSet<Node>();
for (Edge edge : newEdges) {
newNodes.add(edge.source);
newNodes.add(edge.target);
}
Iterator<Edge> edgeIterator = edges.keySet().iterator();
while (edgeIterator.hasNext()) {
Edge edge = edgeIterator.next();
if (!newEdges.contains(edge)) {
Integer row = edges.get(edge);
if (null != row) {
edgeTable.removeRow(row);
}
edgeIterator.remove();
}
}
Iterator<Node> nodeIterator = nodes.keySet().iterator();
while (nodeIterator.hasNext()) {
Node node = nodeIterator.next();
if (!newNodes.contains(node)) {
isGraphChanged = true;
nodeTable.removeRow(nodes.get(node));
nodeIterator.remove();
Node.removeInstance(node.name);
}
}
for (Edge edge : newEdges) {
if (!edges.containsKey(edge)) {
isGraphChanged = true;
Node source = Node.getInstance(edge.source.name);
Integer sourceId = nodes.get(edge.source);
if (null == sourceId) {
sourceId = nodeTable.addRow();
nodeTable.setInt(sourceId, NODE_ID_FIELD, sourceId);
nodeTable.setString(sourceId, NODE_NAME_FIELD,
edge.source.name);
nodes.put(source, sourceId);
}
Node target = Node.getInstance(edge.target.name);
Integer targetId = nodes.get(edge.target);
if (null == targetId) {
targetId = nodeTable.addRow();
nodeTable.setInt(targetId, NODE_ID_FIELD, targetId);
nodeTable.setString(targetId, NODE_NAME_FIELD,
edge.target.name);
nodes.put(target, targetId);
}
if (sourceId != targetId) {
Integer edgeId = edgeTable.addRow();
edgeTable.setInt(edgeId, SOURCE_ID_FIELD, sourceId);
edgeTable.setInt(edgeId, TARGET_ID_FIELD, targetId);
edges.put(edge, edgeId);
} else {
edges.put(edge, null);
}
}
}
newEdges.clear();
if (isGraphChanged) {
// nla.computeStats(edges.keySet());
hasChanged = true;
}
}
private boolean hasChanged = false;
boolean hasChanged() {
boolean hasChanged = this.hasChanged;
this.hasChanged = false;
return hasChanged;
}
Set<String> getAllNodes() {
// Set<String> nodeNames = new HashSet<String>();
// for ( Node node : nodes.keySet() ) nodeNames.add(node.getName());
// return nodeNames;
return Node.getAllNodeNames();
}
int getConnectivity(String node) {
return nla.getNumConnections(node);
}
int getEccentricity(String node) {
return nla.getEccentricity(node);
}
Set<String> getFurthestNodes(String node) {
return nla.getFurthestNodes(node);
}
ArrayList<Integer> getStepCardinalities(String node) {
return nla.getStepCardinalities(node);
}
int getDiameter() {
return nla.getDiameter();
}
int getRadius() {
return nla.getRadius();
}
String getNodesPerEccentricity() {
return nla.getNodesPerEccentricity();
}
String getNodesPerConnectivity() {
return nla.getNodesPerConnectivity();
}
}