package org.ntlab.deltaViewer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JPanel;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedWeightedPseudograph;
import org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.IAliasCollector;
import org.ntlab.trace.MethodExecution;
import com.mxgraph.canvas.mxGraphics2DCanvas;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxICell;
import com.mxgraph.shape.mxConnectorShape;
import com.mxgraph.shape.mxIShape;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.view.mxInteractiveCanvas;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
abstract public class MagnetRONViewer extends JPanel {
protected static Dimension DEFAULT_SIZE = new Dimension(1300, 700);
protected static String WINDOW_TITLE = "Delta Viewer";
protected IAliasCollector aliasCollector;
protected Map<String, ObjectVertex> objectToVertexMap = new HashMap<>();
protected Map<MethodExecution, MethodExecutionVertex> methodExecToVertexMap = new LinkedHashMap<>();
protected Map<String, Edge> edgeMap = new HashMap<>();
protected DeltaGraphAdapter mxgraph;
protected mxICell mxDefaultParent;
protected mxGraphComponent mxgraphComponent;
protected DeltaAnimation deltaAnimation;
protected int curFrame = 0;
public MagnetRONViewer() {
mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph(DefaultEdge.class));
mxDefaultParent = (mxCell)mxgraph.getDefaultParent();
mxgraphComponent = new mxGraphComponent(mxgraph) {
public mxInteractiveCanvas createCanvas() {
return new CurvedCanvas(this);
}
};
deltaAnimation = new DeltaAnimation(mxgraph, mxgraphComponent);
mxgraphComponent.setPreferredSize(DEFAULT_SIZE);
add(mxgraphComponent, BorderLayout.CENTER);
}
/** Set style of All cells. */
protected void setCellsStyle() {
List<Object> vertexObject = new ArrayList<>();
List<Object> staticVertexObject = new ArrayList<>();
List<Object> alignMiddleVertex = new ArrayList<>();
List<Object> alignTopVertex = new ArrayList<>();
List<Object> edgeObject = new ArrayList<>();
List<Object> edgeObjectCreate = new ArrayList<>();
List<Object> edgeMethodExec = new ArrayList<>();
List<Object> roundEdge = new ArrayList<>();
for (Entry<String, ObjectVertex> objectToVertexEntry: objectToVertexMap.entrySet()) {
String key = objectToVertexEntry.getKey();
ObjectVertex objectVertex = objectToVertexEntry.getValue();
if (key.matches("0")) {
staticVertexObject.add(objectVertex.getCell());
} else {
vertexObject.add(objectVertex.getCell());
}
if(objectVertex.getVertexMethodExecutions().size() == 0) {
alignMiddleVertex.add(objectVertex.getCell());
} else {
alignTopVertex.add(objectVertex.getCell());
}
}
List<MethodExecutionVertex> vertexMethodExecList = new ArrayList<>(methodExecToVertexMap.values());
Collections.reverse(vertexMethodExecList);
for (int i = 0; i < vertexMethodExecList.size(); i++) {
switch(i) {
case 0:
((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ff7fbf");
break;
case 1:
((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ff99cc");
break;
case 2:
((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ffb2d8");
break;
case 3:
((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ffcce5");
break;
case 4:
((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ffe0ef");
break;
default:
break;
}
}
for (Edge edge: edgeMap.values()) {
roundEdge.add(edge.getCell());
switch(edge.getTypeName()) {
case Reference:
edgeObject.add(edge.getCell());
break;
case Create:
edgeObject.add(edge.getCell());
edgeObjectCreate.add(edge.getCell());
break;
case Call:
edgeMethodExec.add(edge.getCell());
break;
default:
break;
}
}
/*Given a cell, we can change it's style attributes, for example the color. NOTE that you have to call the graphComponent.refresh() function, otherwise you won't see the difference! */
mxgraph.setCellStyles(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE, vertexObject.toArray(new Object[vertexObject.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE, vertexObject.toArray(new Object[vertexObject.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_UNDERLINE, true, vertexObject.toArray(new Object[vertexObject.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, alignMiddleVertex.toArray(new Object[alignMiddleVertex.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, alignTopVertex.toArray(new Object[alignTopVertex.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM, edgeObject.toArray(new Object[edgeObject.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, edgeObjectCreate.toArray(new Object[edgeObjectCreate.size()]));
// mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ENTITY_RELATION, edgeObject.toArray(new Object[edgeObject.size()]));
// mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ORTHOGONAL, edgeObject.toArray(new Object[edgeObject.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_ROUNDED, 1, true, roundEdge.toArray(new Object[roundEdge.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, roundEdge.toArray(new Object[roundEdge.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_BOTTOM, roundEdge.toArray(new Object[roundEdge.size()]));
// mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.SHAPE_CURVE, edgeObject.toArray(new Object[edgeObject.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_STROKECOLOR, "#008000", edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
// mxgraph.setCellStyleFlags(mxConstants.STYLE_AUTOSIZE, 1, true, vertexObject.toArray(new Object[vertexObject.size()]));
// mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ORTHOGONAL, edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
// mxgraph.setCellStyles(mxConstants.STYLE_ELBOW, mxConstants.ELBOW_VERTICAL, edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
}
abstract public void initAnimation();
/**
* Step to animation of specified alias.
*
* @param alias Alias type and occurrence point etc.
*/
abstract public void stepToAnimation(Alias alias);
/**
* Parent : Step to animation of specified numFrame.
*
* @param numFrame Current animation frame.
*/
abstract public void stepToAnimation(int numFrame);
/** Update graph on JFrame and set Cell style. */
protected void update() {
setCellsStyle();
mxgraphComponent.refresh();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void setFrameSize(int width, int height) {
DEFAULT_SIZE.setSize(width, height);
}
/**
* Whether sourceCell parents contain targetCell.
* @param sourceCell
* @param targetCell
* @return
*/
protected boolean isParent(mxICell sourceCell, mxICell targetCell) {
mxICell sourceParentCell = sourceCell.getParent();
if (sourceParentCell == null || sourceParentCell.getValue() == null) {
return false;
}
if (sourceParentCell == targetCell) {
return true;
}
System.out.println(sourceCell.getId() + ", " + sourceParentCell.getId());
return isParent(sourceParentCell, targetCell);
}
protected Point getAbsolutePointforCell(mxICell cell) {
Point p1 = cell.getGeometry().getPoint();
if(cell.getParent().getValue() == null || cell == cell.getParent()) {
return p1;
}
System.out.println(cell.getId() + ", " + cell.getParent().getId());
Point p2 = getAbsolutePointforCell(cell.getParent());
return new Point((int) (p1.getX() + p2.getX()), (int) (p1.getY() + p2.getY()));
}
protected void reflectCoordinates(DeltaGraphAdapter mxgraph) {
// TODO Auto-generated method stub
}
protected class CurvedCanvas extends mxInteractiveCanvas {
mxIShape shape = new CurvedConnector();
public CurvedCanvas(mxGraphComponent mxGraphComponent) {
super(mxGraphComponent);
}
public Object drawCell(mxCellState state) {
if (!(state.getCell() instanceof mxCell) || !((mxCell)state.getCell()).isEdge() || state.getAbsolutePointCount() == 2) {
return super.drawCell(state);
}
Map<String, Object> style = state.getStyle();
if (g != null) {
// Creates a temporary graphics instance for drawing this shape
float opacity = mxUtils.getFloat(style, mxConstants.STYLE_OPACITY, 100);
Graphics2D previousGraphics = g;
g = createTemporaryGraphics(style, opacity, state);
shape.paintShape(this, state);
g.dispose();
g = previousGraphics;
}
return shape;
}
}
protected class CurvedConnector extends mxConnectorShape {
public void paintShape(mxGraphics2DCanvas canvas, mxCellState state) {
if (state.getAbsolutePointCount() > 1
&& configureGraphics(canvas, state, false)) {
List<mxPoint> pts = new ArrayList<mxPoint>(
state.getAbsolutePoints());
Map<String, Object> style = state.getStyle();
// Paints the markers and updates the points
// Switch off any dash pattern for markers
boolean dashed = mxUtils.isTrue(style, mxConstants.STYLE_DASHED);
Object dashedValue = style.get(mxConstants.STYLE_DASHED);
if (dashed) {
style.remove(mxConstants.STYLE_DASHED);
canvas.getGraphics().setStroke(canvas.createStroke(style));
}
translatePoint(pts, 0,
paintMarker(canvas, state, true));
translatePoint(
pts,
pts.size() - 1,
paintMarker(canvas, state, false));
if (dashed) {
// Replace the dash pattern
style.put(mxConstants.STYLE_DASHED, dashedValue);
canvas.getGraphics().setStroke(canvas.createStroke(style));
}
// Paints the shape and restores the graphics object
if (state.getAbsolutePointCount() == 4) {
double sx = state.getAbsolutePoint(0).getX();
double sy = state.getAbsolutePoint(0).getY();
double tx1 = state.getAbsolutePoint(1).getX();
double ty1 = state.getAbsolutePoint(1).getY();
double tx2 = state.getAbsolutePoint(2).getX();
double ty2 = state.getAbsolutePoint(2).getY();
double ex = state.getAbsolutePoint(3).getX();
double ey = state.getAbsolutePoint(3).getY();
Path2D.Double p = new Path2D.Double();
p.moveTo((int) sx, (int) sy);
p.quadTo((int) tx2, (int) ty2, (int) ex, (int) ey);
// p.curveTo((int) tx1, (int) ty1, (int) tx2, (int) ty2, (int) ex, (int) ey);
canvas.getGraphics().draw(p);
} else if (state.getAbsolutePointCount() == 3) {
double sx = state.getAbsolutePoint(0).getX();
double sy = state.getAbsolutePoint(0).getY();
double tx = state.getAbsolutePoint(1).getX();
double ty = state.getAbsolutePoint(1).getY();
double ex = state.getAbsolutePoint(2).getX();
double ey = state.getAbsolutePoint(2).getY();
Path2D.Double p = new Path2D.Double();
p.moveTo((int) sx, (int) sy);
p.quadTo((int) tx, (int) ty, (int) ex, (int) ey);
canvas.getGraphics().draw(p);
}
}
}
private void translatePoint(List<mxPoint> points, int index, mxPoint offset) {
if (offset != null) {
mxPoint pt = (mxPoint) points.get(index).clone();
pt.setX(pt.getX() + offset.getX());
pt.setY(pt.getY() + offset.getY());
points.set(index, pt);
}
}
}
}