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); } } } }