package org.ntlab.deltaViewer; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Point; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.JFrame; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedWeightedPseudograph; import org.ntlab.deltaExtractor.Alias; import org.ntlab.deltaExtractor.Delta; import org.ntlab.deltaExtractor.ExtractedStructure; import org.ntlab.deltaViewer.Edge.TypeName; import org.ntlab.deltaExtractor.Alias.AliasType; import org.ntlab.trace.FieldAccess; import org.ntlab.trace.FieldUpdate; import org.ntlab.trace.MethodExecution; import org.ntlab.trace.MethodInvocation; import org.ntlab.trace.Reference; import org.ntlab.trace.Statement; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxPoint; import com.mxgraph.view.mxGraphView; /** * Make and display JGraph of extracted delta. * * @author Nitta Lab. */ public class DeltaViewer { private static Dimension DEFAULT_SIZE = new Dimension(700, 700); private static String WINDOW_TITLE = "Delta Viewer"; private ExtractedStructure eStructure; private DeltaAliasCollector deltaAliasCollector; private List<Alias> aliasList; private Map<String, VertexObject> objectToVertexMap = new HashMap<>(); private Map<MethodExecution, VertexMethodExecution> methodExecToVertexMap = new LinkedHashMap<>(); private Map<String, Edge> edgeMap = new HashMap<>(); private JFrame frame; private DeltaGraphAdapter mxgraph; // No clue what this does but it is needed. private mxICell mxDefaultParent; private mxGraphComponent mxgraphComponent; private int curNumFrame = 0; private static Dimension VERTEX_OBJECT_SIZE = new Dimension(70, 70); private static Dimension VERTEX_METHOD_EXECUTION_SIZE = new Dimension(55, 20); private mxPoint coordinatorPoint = new mxPoint(DEFAULT_SIZE.getWidth() / 2 - 50, 100); private DeltaAnimation deltaAnimation; private double scale = 1; public DeltaViewer() { mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph(DefaultEdge.class)); mxDefaultParent = (mxCell)mxgraph.getDefaultParent(); mxgraphComponent = new mxGraphComponent(mxgraph); deltaAnimation = new DeltaAnimation(mxgraph, mxgraphComponent); } public DeltaViewer(ExtractedStructure extractedStructure, DeltaAliasCollector deltaAliasCollector) { this(); this.eStructure = extractedStructure; this.deltaAliasCollector = deltaAliasCollector; aliasList = new ArrayList<>(deltaAliasCollector.getAliasList()); // init(); } /** Initialize JFrame, make vertex object and edge object. */ public void init() { // Build a frame, create a graph, and add the graph to the frame so you can actually see the graph. if(eStructure != null) { WINDOW_TITLE = "extract delta of:" + eStructure.getDelta().getSrcSide().get(0).getDstClassName() + "(" + eStructure.getDelta().getSrcSide().get(0).getDstObjectId() + ")" + " -> " + eStructure.getDelta().getDstSide().get(0).getDstClassName() + "(" + eStructure.getDelta().getDstSide().get(0).getDstObjectId() + ")"; } frame = new JFrame(WINDOW_TITLE); frame.setSize(DEFAULT_SIZE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(mxgraphComponent, BorderLayout.CENTER); frame.setVisible(true); while(coordinatorPoint.getX() - (150 * (eStructure.getDelta().getDstSide().size())) < 0) { coordinatorPoint.setX(coordinatorPoint.getX() + 150); } makeVertexObject(); makeEdgeObject(); // Fit graph size in visiable JFrame. mxGraphView view = mxgraphComponent.getGraph().getView(); int componentWidth = mxgraphComponent.getWidth(); int viewWidth = (int) view.getGraphBounds().getWidth(); scale = (double)componentWidth/viewWidth; view.setScale(scale); deltaAnimation.setScale(scale); System.out.println(componentWidth + ", " + viewWidth + ", " + scale); } /** Display graph on JFrame. */ public void display() { setCellStyles(); mxgraphComponent.refresh(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } public void setExtractedStructure(ExtractedStructure extractedStructure) { this.eStructure = extractedStructure; } public void setDeltaAliasCollector(DeltaAliasCollector deltaAliasCollector) { this.deltaAliasCollector = deltaAliasCollector; } public void setFrameSize(int width, int height) { DEFAULT_SIZE.setSize(width, height); } public void setCoordinatorPoint(double x, double y) { coordinatorPoint.setX(x); coordinatorPoint.setY(y); } /** Set cell styles. */ private void setCellStyles() { List<Object> vertexObject = new ArrayList<>(); List<Object> alignMiddleVertex = new ArrayList<>(); List<Object> alignTopVertex = new ArrayList<>(); List<Object> edgeObject = new ArrayList<>(); List<Object> edgeMethodExec = new ArrayList<>(); List<Object> roundEdge = new ArrayList<>(); for (VertexObject vertex: objectToVertexMap.values()) { vertexObject.add(vertex.getCell()); if(vertex.getMethodExecutions().size() == 0) { alignMiddleVertex.add(vertex.getCell()); } else { alignTopVertex.add(vertex.getCell()); } } List<VertexMethodExecution> 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 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.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()])); } private double getXForCell(String id) { double res = -1; if (objectToVertexMap.containsKey(id)) { Object cell = objectToVertexMap.get(id).getCell(); res = mxgraph.getCellGeometry(cell).getX(); } return res; } private double getYForCell(String id) { double res = -1; if (objectToVertexMap.containsKey(id)) { Object cell = objectToVertexMap.get(id).getCell(); res = mxgraph.getCellGeometry(cell).getY(); } return res; } private mxICell getRootParentCell(Object object) { mxICell cell = (mxICell) object; if(cell.getParent().getValue() == null) { return cell; } return getRootParentCell(cell.getParent()); } private Point getAbsolutePointforCell(Object object) { mxICell cell = (mxICell) object; Point p1 = cell.getGeometry().getPoint(); if(cell.getParent().getValue() == null) { return p1; } Point p2 = getAbsolutePointforCell(cell.getParent()); return new Point((int) (p1.getX() + p2.getX()), (int) (p1.getY() + p2.getY())); } /** * Step to animation of specified alias. * * @param alias Alias type and occurance point etc. */ public void stepToAnimation(Alias alias) { try { stepToAnimation(aliasList.indexOf(alias)); } catch (IndexOutOfBoundsException e) { stepToAnimation(-1); } } /** * Parent : Step to animation of specified numFrame. * * @param numFrame Current animation frame. */ public void stepToAnimation(int numFrame) { try { Alias alias = aliasList.get(numFrame); makeAnimation(aliasList.get(curNumFrame + 1), alias); curNumFrame = aliasList.indexOf(alias); } catch (IndexOutOfBoundsException e) { if (numFrame == - 1) { System.out.println("ERROR : Not exist alias."); } else { System.out.println("\r\nLast Animation."); makeLastAnimation(aliasList.get(aliasList.size() - 1)); } } } /** * Make animation from fromAlias to toAlias. * * @param fromAlias * @param toAlias */ private void makeAnimation(Alias fromAlias, Alias toAlias) { for (int i = aliasList.indexOf(fromAlias); i <= aliasList.indexOf(toAlias); i++) { Alias alias = aliasList.get(i); System.out.println("\r\n" + alias.getObjectId() + ", " + alias.getMethodSignature() + " l." + alias.getLineNo() + " : " + alias.getAliasType().toString()); switch(alias.getAliasType()) { case RETURN_VALUE: moveObjectVertex(alias); break; case METHOD_INVOCATION: removeVertexMethodExecution(alias); moveObjectVertex(alias); break; case FORMAL_PARAMETER: moveObjectVertex(alias); break; case ACTUAL_ARGUMENT: moveObjectVertex(alias); break; case THIS: if (curNumFrame == 0) { makeVertexMethodExecution(alias); } break; case RECEIVER: // Make VertexMethodExecution of called method execution. MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution(); if (!methodExecToVertexMap.containsKey(calledMethodExec)) { makeVertexMethodExecution(alias, calledMethodExec.getSignature(), calledMethodExec); } break; default: break; } } } /** * Make last animation of extracted delta. * * @param alias Last index alias. */ private void makeLastAnimation(Alias alias) { // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { // Make ObjectEdge and reset position of vertexObject, remove vertexMethodExecution. for(Statement statement: alias.getMethodExecution().getStatements()) { if(statement instanceof FieldUpdate) { FieldUpdate fieldUpdateStatement = (FieldUpdate) statement; String fieldNames[] = fieldUpdateStatement.getFieldName().split("\\."); String fieldName = fieldNames[1]; String sourceObjectId = fieldUpdateStatement.getContainerObjId(); String targetObjectId = fieldUpdateStatement.getValueObjId(); mxICell sourceCell = (mxICell)objectToVertexMap.get(sourceObjectId).getCell(); mxICell targetCell = (mxICell)objectToVertexMap.get(targetObjectId).getCell(); Point absolutePointTargetCell = getAbsolutePointforCell(targetCell); Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, fieldUpdateStatement.getFieldName(), fieldName, objectToVertexMap.get(sourceObjectId).getCell(), objectToVertexMap.get(targetObjectId).getCell()); edgeMap.put(fieldUpdateStatement.getFieldName(), new Edge(fieldName, TypeName.Reference, edge)); System.out.println("last" + objectToVertexMap.get(targetObjectId).getInitialX() + ", " + objectToVertexMap.get(targetObjectId).getInitialY()); System.out.println(targetCell.getParent()); targetCell.getParent().remove(targetCell); targetCell.setParent(mxDefaultParent); targetCell.getGeometry().setX(absolutePointTargetCell.getX()); targetCell.getGeometry().setY(absolutePointTargetCell.getY()); deltaAnimation.setVertexAnimation(targetCell, new mxPoint(objectToVertexMap.get(targetObjectId).getInitialX(), objectToVertexMap.get(targetObjectId).getInitialY())); deltaAnimation.startVertexAnimation(); System.out.println(targetCell.getParent()); targetCell.getGeometry().setX(objectToVertexMap.get(targetObjectId).getInitialX()); targetCell.getGeometry().setY(objectToVertexMap.get(targetObjectId).getInitialY()); removeCalledVertexMethodExecution(objectToVertexMap.get(sourceObjectId), alias.getMethodExecution().getCallerMethodExecution(), alias.getMethodExecution()); updateVertexObjectSize(); display(); } } } finally { mxgraph.getModel().endUpdate(); } // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { List<MethodExecution> methodExecList = new ArrayList<>(methodExecToVertexMap.keySet()); Collections.reverse(methodExecList); for(int i = 0; i < methodExecList.size() - 1; i++) { String objectId = methodExecList.get(i).getThisObjId(); VertexObject sourceVertexObject = objectToVertexMap.get(objectId); // sourceVertex MethodExecution methodExec = methodExecList.get(i); for(Statement statement: methodExec.getStatements()) { if(statement instanceof MethodInvocation) { MethodExecution calledMethodExec = ((MethodInvocation) statement).getCalledMethodExecution(); String calledObjectId = calledMethodExec.getThisObjId(); mxICell calledCell = (mxICell)objectToVertexMap.get(calledObjectId).getCell(); Point absolutePointCalledCell = getAbsolutePointforCell(calledCell); System.out.println(objectId + ", " + methodExec.getSignature()); // objectToVertexMap.get(calledObjectId).resetCellPosition(); if (methodExecToVertexMap.get(methodExec).getArguments().contains(objectToVertexMap.get(calledObjectId)) || methodExecToVertexMap.get(methodExec).getLocals().contains(objectToVertexMap.get(calledObjectId))) { calledCell.getParent().remove(calledCell); calledCell.setParent(mxDefaultParent); calledCell.getGeometry().setX(absolutePointCalledCell.getX()); calledCell.getGeometry().setY(absolutePointCalledCell.getY()); deltaAnimation.setVertexAnimation(calledCell, new mxPoint(objectToVertexMap.get(calledObjectId).getInitialX(), objectToVertexMap.get(calledObjectId).getInitialY())); deltaAnimation.startVertexAnimation(); } removeCalledVertexMethodExecution(sourceVertexObject, methodExec.getCallerMethodExecution(), methodExec); updateVertexObjectSize(); // removeVertexMethodExecution(sourceVertexObject, methodExec); display(); break; } } } } finally { mxgraph.getModel().endUpdate(); } } /** Make VertexObject. */ private void makeVertexObject() { //Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. // �¶�ã(0, 0) double xCor = coordinatorPoint.getX(); double yCor = coordinatorPoint.getY(); double time = 150; mxgraph.getModel().beginUpdate(); try { // Draw vertex object. // srcSide Delta delta = eStructure.getDelta(); int srcSideSize = delta.getSrcSide().size(); for (int i = srcSideSize - 1; i >= 0; i--) { Reference ref = delta.getSrcSide().get(i); if (!ref.isCreation() || !ref.getSrcObjectId().equals(ref.getDstObjectId())) { if(!objectToVertexMap.containsKey(ref.getSrcObjectId())) { Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, ref.getSrcClassName(), ref.getSrcClassName(), xCor + (time * ((srcSideSize - 1) - i)), yCor + (time * ((srcSideSize - 1) - i)), VERTEX_OBJECT_SIZE.getWidth(), VERTEX_OBJECT_SIZE.getHeight(), "fillColor=white"); //creates a white vertex. // mxgraph.updateCellSize(vertex, true); objectToVertexMap.put(ref.getSrcObjectId(), new VertexObject(ref.getSrcClassName(), vertex, xCor + (time * ((srcSideSize - 1) - i)), yCor + (time * ((srcSideSize - 1) - i)))); } if(!objectToVertexMap.containsKey(ref.getDstObjectId())) { Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, ref.getDstClassName(), ref.getDstClassName(), xCor + (time * (srcSideSize - i)), yCor + (time * (srcSideSize - i)), VERTEX_OBJECT_SIZE.getWidth(), VERTEX_OBJECT_SIZE.getHeight(), "fillColor=white"); //creates a white vertex. objectToVertexMap.put(ref.getDstObjectId(), new VertexObject(ref.getDstClassName(), vertex, xCor + (time * (srcSideSize - i)), yCor + (time * (srcSideSize - i)))); } } } // dstSide int dstSideSize = delta.getDstSide().size(); for (int i = dstSideSize - 1; i >= 0; i--) { Reference ref = delta.getDstSide().get(i); if (!ref.isCreation() || !ref.getSrcObjectId().equals(ref.getDstObjectId())) { Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, ref.getDstClassName(), ref.getDstClassName(), xCor - (time * (dstSideSize - i)), yCor + (time * (dstSideSize - i)), VERTEX_OBJECT_SIZE.getWidth(), VERTEX_OBJECT_SIZE.getHeight(), "fillColor=white"); //creates a white vertex. objectToVertexMap.put(ref.getDstObjectId(), new VertexObject(ref.getDstClassName(), vertex, xCor - (time * (dstSideSize - i)), yCor + (time * (dstSideSize - i)))); } } } finally { mxgraph.getModel().endUpdate(); } } /** * SourceVertex move targetVertex. * * @param alias */ private void moveObjectVertex(Alias alias) { // sourceVertex VertexObject sourceVertexObject = objectToVertexMap.get(alias.getObjectId()); // targetVertex VertexMethodExecution targetVertexMethodExec = methodExecToVertexMap.get(alias.getMethodExecution()); moveObjectVertex(alias, sourceVertexObject, targetVertexMethodExec); updateVertexObjectSize(); } /** * Parent : Source VertexObject move target VertexMethodExecution. * * @param alias * @param sourceVertexObject Source VertexObject. * @param targetVertexMethodExec Target VertexMethodExecution. */ private void moveObjectVertex(Alias alias, VertexObject sourceVertexObject, VertexMethodExecution targetVertexMethodExec) { MethodExecution methodExec = alias.getMethodExecution(); if (alias.getAliasType().equals(AliasType.RETURN_VALUE) || alias.getAliasType().equals(AliasType.METHOD_INVOCATION)) { moveLocalObjectVertex(methodExec, sourceVertexObject, targetVertexMethodExec); } else if (alias.getAliasType().equals(AliasType.FORMAL_PARAMETER)) { moveArgumentObjectVertex(methodExec, sourceVertexObject, targetVertexMethodExec); } else if (alias.getAliasType().equals(AliasType.ACTUAL_ARGUMENT)) { moveActualArgumentObjectVertex(methodExec, sourceVertexObject, targetVertexMethodExec); } } /** * Source VertexObject move target VertexMethodExecution to Local position from caller MethodExecution. * * @param callerMethodExec Caller MethodExecution. * @param sourceVertexObject * @param targetVertexMethodExec */ private void moveLocalObjectVertex(MethodExecution callerMethodExec, VertexObject sourceVertexObject, VertexMethodExecution targetVertexMethodExec) { mxICell sourceCell = (mxICell)sourceVertexObject.getCell(); mxICell targetCell = (mxICell) targetVertexMethodExec.getCell(); // mxICell parentTargetMethodExecCell = targetMethodExecCell.getParent(); // Remove sourceVertex from Locals and Arguments Vertex of MethodExecution's Vertex. MethodExecution callercallerMethodExec = callerMethodExec.getCallerMethodExecution(); if (methodExecToVertexMap.containsKey(callercallerMethodExec) && methodExecToVertexMap.get(callercallerMethodExec).getLocals().contains(sourceVertexObject)) { methodExecToVertexMap.get(callercallerMethodExec).getLocals().remove(sourceVertexObject); System.out.println(methodExecToVertexMap.get(callercallerMethodExec).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel()); } if (methodExecToVertexMap.containsKey(callercallerMethodExec) && methodExecToVertexMap.get(callercallerMethodExec).getArguments().contains(sourceVertexObject)) { methodExecToVertexMap.get(callercallerMethodExec).getArguments().remove(sourceVertexObject); System.out.println(methodExecToVertexMap.get(callercallerMethodExec).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel()); } // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { int time = targetVertexMethodExec.getLocals().size() + 1; double sourceX = sourceCell.getGeometry().getX(); double sourceY = sourceCell.getGeometry().getY(); System.out.println(time); if(sourceCell.getParent().getValue() != null) { Point absolutePointSourceCell = getAbsolutePointforCell(sourceCell); sourceX = absolutePointSourceCell.getX(); sourceY = absolutePointSourceCell.getY(); sourceCell.getParent().remove(sourceCell); } sourceCell.setParent(targetCell.getParent()); targetCell.getParent().insert(sourceCell); Point absolutePointTargetCell = getAbsolutePointforCell(sourceCell.getParent()); sourceCell.getGeometry().setX(sourceX - absolutePointTargetCell.getX()); sourceCell.getGeometry().setY(sourceY - absolutePointTargetCell.getY()); double sourceWidth = sourceCell.getGeometry().getWidth(); double sourceHeight = sourceCell.getGeometry().getHeight(); double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1); double overlapHeight = sourceHeight - (sourceHeight * Math.sqrt(2) * 0.1); deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth, targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time))); deltaAnimation.startVertexAnimation(); sourceCell.setParent(targetCell.getParent()); targetCell.getParent().insert(sourceCell); sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth); sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time)); targetVertexMethodExec.getLocals().add(sourceVertexObject); System.out.println(targetVertexMethodExec.getLabel() + " :Local: " + sourceVertexObject.getLabel()); } finally { mxgraph.getModel().endUpdate(); } } /** * Source VertexObject move target VertexMethodExecution to Argument position from MethodExecution. * * @param methodExec MethodExecution. * @param sourceVertexObject * @param targetVertexMethodExec */ private void moveArgumentObjectVertex(MethodExecution methodExec, VertexObject sourceVertexObject, VertexMethodExecution targetVertexMethodExec) { mxICell sourceCell = (mxICell)sourceVertexObject.getCell(); mxICell targetCell = (mxICell) targetVertexMethodExec.getCell(); // mxICell parentTargetMethodExecCell = targetMethodExecCell.getParent(); // Remove sourceVertex from Locals and Arguments Vertex of MethodExecution's Vertex. MethodExecution callerMethodExecution = methodExec.getCallerMethodExecution(); if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getLocals().contains(sourceVertexObject)) { methodExecToVertexMap.get(callerMethodExecution).getLocals().remove(sourceVertexObject); System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel()); } if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getArguments().contains(sourceVertexObject)) { methodExecToVertexMap.get(callerMethodExecution).getArguments().remove(sourceVertexObject); System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel()); } // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { int time = targetVertexMethodExec.getArguments().size(); double sourceX = sourceCell.getGeometry().getX(); double sourceY = sourceCell.getGeometry().getY(); System.out.println(time); if(sourceCell.getParent().getValue() != null) { Point absolutePointSourceCell = getAbsolutePointforCell(sourceCell); sourceX = absolutePointSourceCell.getX(); sourceY = absolutePointSourceCell.getY(); sourceCell.getParent().remove(sourceCell); } sourceCell.setParent(targetCell.getParent()); targetCell.getParent().insert(sourceCell); sourceCell.getGeometry().setX(sourceX - sourceCell.getParent().getGeometry().getX()); sourceCell.getGeometry().setY(sourceY - sourceCell.getParent().getGeometry().getY()); double sourceWidth = sourceCell.getGeometry().getWidth(); double sourceHeight = sourceCell.getGeometry().getHeight(); double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1); double overlapHeight = sourceHeight - (sourceHeight * Math.sqrt(2) * 0.1); deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth, targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time))); deltaAnimation.startVertexAnimation(); // sourceCell.setParent(targetCell.getParent()); // targetCell.getParent().insert(sourceCell); sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth); sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time)); targetVertexMethodExec.getArguments().add(sourceVertexObject); System.out.println(targetVertexMethodExec.getLabel() + " :Argument: " + sourceVertexObject.getLabel()); } finally { mxgraph.getModel().endUpdate(); } } /** * Source VertexObject move target VertexMethodExecution to Argument position from MethodExecution. * * @param methodExec MethodExecution. * @param sourceVertexObject * @param targetVertexMethodExec */ private void moveActualArgumentObjectVertex(MethodExecution methodExec, VertexObject sourceVertexObject, VertexMethodExecution targetVertexMethodExec) { mxICell sourceCell = (mxICell)sourceVertexObject.getCell(); mxICell targetCell = (mxICell) targetVertexMethodExec.getCell(); // mxICell parentTargetMethodExecCell = targetMethodExecCell.getParent(); // Remove sourceVertex from Locals and Arguments Vertex of MethodExecution's Vertex. MethodExecution callerMethodExecution = methodExec.getCallerMethodExecution(); if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getLocals().contains(sourceVertexObject)) { methodExecToVertexMap.get(callerMethodExecution).getLocals().remove(sourceVertexObject); System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel()); } if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getArguments().contains(sourceVertexObject)) { methodExecToVertexMap.get(callerMethodExecution).getArguments().remove(sourceVertexObject); System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel()); } // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { int time = targetVertexMethodExec.getArguments().size(); double sourceX = sourceCell.getGeometry().getX(); double sourceY = sourceCell.getGeometry().getY(); System.out.println(time); if (time == 0) time = 1; if(sourceCell.getParent().getValue() != null) { Point absolutePointSourceCell = getAbsolutePointforCell(sourceCell); sourceX = absolutePointSourceCell.getX(); sourceY = absolutePointSourceCell.getY(); sourceCell.getParent().remove(sourceCell); } sourceCell.setParent(targetCell.getParent()); targetCell.getParent().insert(sourceCell); sourceCell.getGeometry().setX(sourceX - sourceCell.getParent().getGeometry().getX()); sourceCell.getGeometry().setY(sourceY - sourceCell.getParent().getGeometry().getY()); double sourceWidth = sourceCell.getGeometry().getWidth(); double sourceHeight = sourceCell.getGeometry().getHeight(); double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1); double overlapHeight = sourceHeight - (sourceHeight * Math.sqrt(2) * 0.1); deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth, targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time))); deltaAnimation.startVertexAnimation(); sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth); sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time)); targetVertexMethodExec.getArguments().add(sourceVertexObject); System.out.println(targetVertexMethodExec.getLabel() + " :Argument: " + sourceVertexObject.getLabel()); } finally { mxgraph.getModel().endUpdate(); } } /** Update VertexObject size. */ private void updateVertexObjectSize() { // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology mxgraph.getModel().beginUpdate(); try { for (VertexObject vertexObject: objectToVertexMap.values()) { mxCell vertexObjectCell = ((mxCell) vertexObject.getCell()); int time = vertexObjectCell.getChildCount(); if (time == 0) { time = 1; } if(vertexObjectCell.getGeometry().getWidth() != VERTEX_OBJECT_SIZE.getWidth() * time) { Dimension targetDimension = new Dimension(); targetDimension.setSize(VERTEX_OBJECT_SIZE.getWidth() * time, VERTEX_OBJECT_SIZE.getHeight() * time); deltaAnimation.setResizeVertexAnimation(vertexObjectCell, targetDimension); deltaAnimation.startResizeVertexAnimation(); } } }finally { mxgraph.getModel().endUpdate(); } show(mxDefaultParent); } private void show(mxICell cell) { for(Object object: mxgraph.getChildCells(cell)) { System.out.println(object); } } /** * Update VertexObject of targetMethodExecCell size have sourceObjectCell. * * @param sourceObjectCell * @param targetMethodExecCell */ private void updateVertexObjectSize(mxICell sourceObjectCell, mxICell targetMethodExecCell) { mxICell parentTargetMethodExecCell = targetMethodExecCell.getParent(); //Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { double preX = parentTargetMethodExecCell.getGeometry().getX(); double preY = parentTargetMethodExecCell.getGeometry().getY(); double preCenterX = parentTargetMethodExecCell.getGeometry().getCenterX(); double preCenterY = parentTargetMethodExecCell.getGeometry().getCenterY(); parentTargetMethodExecCell.getGeometry().setWidth(parentTargetMethodExecCell.getGeometry().getWidth() * 1.8); parentTargetMethodExecCell.getGeometry().setHeight(parentTargetMethodExecCell.getGeometry().getHeight() * 1.8); parentTargetMethodExecCell.getGeometry().setX(preX - (parentTargetMethodExecCell.getGeometry().getCenterX() - preCenterX)); parentTargetMethodExecCell.getGeometry().setY(preY - (parentTargetMethodExecCell.getGeometry().getCenterY() - preCenterY)); } finally { mxgraph.getModel().endUpdate(); } } /** Make edge object in JGraphT and draw this in JGraphX. */ private void makeEdgeObject() { Map<String, Collection<String>> fieldNameMap = new HashMap<>(); // Format field name. for(Alias alias: aliasList) { if(alias.getAliasType().equals(AliasType.THIS)) { for(Statement statement: alias.getMethodExecution().getStatements()) { if(statement instanceof FieldAccess) { String fieldNames[] = formatFieldName(((FieldAccess)statement).getFieldName()); if(fieldNameMap.get(fieldNames[0]) == null) { fieldNameMap.put(fieldNames[0], new HashSet<String>()); } fieldNameMap.get(fieldNames[0]).add(fieldNames[1]); } } } } mxgraph.getModel().beginUpdate(); try { // Make object edge in JGraphT and draw this in JGraphX. Delta delta = eStructure.getDelta(); int srcSideSize = delta.getSrcSide().size(); int dstSideSize = delta.getDstSide().size(); for (int i = srcSideSize - 1; i >= 0; i--) { Reference ref = delta.getSrcSide().get(i); if (!ref.isCreation() || !ref.getSrcObjectId().equals(ref.getDstObjectId())) { for(String fieldName: fieldNameMap.get(ref.getSrcClassName())) { // BUG : contains -> equals if(ref.getDstClassName().contains(fieldName.toUpperCase())) { Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, fieldName, fieldName, objectToVertexMap.get(ref.getSrcObjectId()).getCell(), objectToVertexMap.get(ref.getDstObjectId()).getCell()); ((mxCell)edge).setStyle("exitX=1;exitY=1;exitPerimeter=1;entryX=0;entryY=0;entryPerimeter=1;"); edgeMap.put(ref.getSrcClassName() + "." + fieldName, new Edge(fieldName, TypeName.Reference, edge)); break; } } } } for (int i = dstSideSize - 1; i >= 0; i--) { Reference ref = delta.getDstSide().get(i); if (!ref.isCreation() || !ref.getSrcObjectId().equals(ref.getDstObjectId())) { for (String fieldName: fieldNameMap.get(ref.getSrcClassName())) { // BUG : contains -> equals if (ref.getDstClassName().contains(fieldName.toUpperCase())) { Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, fieldName, fieldName, objectToVertexMap.get(ref.getSrcObjectId()).getCell(), objectToVertexMap.get(ref.getDstObjectId()).getCell()); ((mxCell)edge).setStyle("exitX=0;exitY=1;exitPerimeter=1;entryX=1;entryY=0;entryPerimeter=1;"); edgeMap.put(ref.getSrcClassName() + "." + fieldName, new Edge(fieldName, TypeName.Reference, edge)); break; } } } } } finally { mxgraph.getModel().endUpdate(); } } /** * Make VertexMethodExecution. * * @param alias */ private void makeVertexMethodExecution(Alias alias) { makeVertexMethodExecution(alias, alias.getMethodSignature(), alias.getMethodExecution()); } /** * Parent : Make VertexMethodExecution. * * @param alias * @param methodSignature Called or this MethodSignature. * @param methodExec Called or this MethodExecution. */ private void makeVertexMethodExecution(Alias alias, String methodSignature, MethodExecution methodExec) { if (methodSignature.contains(" ")) { methodSignature = formatMethodSignature(methodSignature); } // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { String objectId = alias.getObjectId(); Object object = objectToVertexMap.get(objectId).getCell(); double xCor = VERTEX_OBJECT_SIZE.getWidth() * 0.1; double yCor = VERTEX_OBJECT_SIZE.getHeight() * 0.5; int time = objectToVertexMap.get(objectId).getMethodExecutions().size(); Object vertex = mxgraph.insertDeltaVertex(object, methodSignature, methodSignature, "fillColor=white"); //creates a white vertex. System.out.println((mxICell)vertex); VertexMethodExecution vertexMethodExecution = new VertexMethodExecution(methodSignature, vertex, xCor * (time + 1), yCor * (time + 1), VERTEX_METHOD_EXECUTION_SIZE.getWidth(), VERTEX_METHOD_EXECUTION_SIZE.getHeight()); // Object vertex = mxgraph.insertVertex(object, methodSignature, methodSignature, 0, 0, 0, 0, "fillColor=white", true); //creates a white vertex. // Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, methodSignature, methodSignature, "fillColor=white"); //creates a white vertex. // VertexMethodExecution vertexMethodExecution = new VertexMethodExecution(methodSignature, vertex, getXForCell(objectId) + (xCor * (time + 1)), getYForCell(objectId) + (yCor * (time + 1)), VERTEX_METHOD_EXECUTION_SIZE.getWidth(), VERTEX_METHOD_EXECUTION_SIZE.getHeight()); methodExecToVertexMap.put(methodExec, vertexMethodExecution); if(methodExecToVertexMap.size() > 1) { ((mxICell)vertex).setVisible(false); } objectToVertexMap.get(objectId).addMethodExecution(vertexMethodExecution); } finally { mxgraph.getModel().endUpdate(); } makeEdgeMethodExecution(); } /** * Remove VertexMethodExecution on AliasType is MethodInvocation of alias. * * @param alias */ private void removeVertexMethodExecution(Alias alias) { // sourceVertex VertexObject sourceVertexObject = objectToVertexMap.get(alias.getObjectId()); MethodExecution methodExec = alias.getMethodExecution(); if(alias.getAliasType().equals(AliasType.METHOD_INVOCATION)) { MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution(); removeCalledVertexMethodExecution(sourceVertexObject, methodExec, calledMethodExec); } else { removeVertexMethodExecution(sourceVertexObject, methodExec); } } /** * Remove VertexMethodExecution on AliasType is MethodInvocation of alias. * * @param sourceVertexObject * @param methodExec */ private void removeVertexMethodExecution(VertexObject sourceVertexObject, MethodExecution methodExec) { // Remove sourceVertex from Locals and Arguments Vertex of CalledMethodExecution's Vertex. if (methodExecToVertexMap.containsKey(methodExec)) { mxCell targetVertexCell = (mxCell)methodExecToVertexMap.get(methodExec).getCell(); targetVertexCell.getParent().remove(targetVertexCell); targetVertexCell.setParent(mxDefaultParent); mxgraph.removeCells(new Object[] {targetVertexCell}); objectToVertexMap.get(methodExec.getThisObjId()).getMethodExecutions().remove(methodExecToVertexMap.get(methodExec)); methodExecToVertexMap.remove(methodExec); edgeMap.remove(methodExec.getSignature()); updateVertexObjectSize(); } } /** * Remove CalledVertexMethodExecution on AliasType is MethodInvocation of alias. * * @param sourceVertexObject * @param methodExec * @param calledMethodExec */ private void removeCalledVertexMethodExecution(VertexObject sourceVertexObject, MethodExecution methodExec, MethodExecution calledMethodExec) { // Remove sourceVertex from Locals and Arguments Vertex of CalledMethodExecution's Vertex. if (methodExecToVertexMap.containsKey(calledMethodExec)) { mxICell sourceVertexCell = (mxICell)methodExecToVertexMap.get(methodExec).getCell(); mxCell targetVertexCell = (mxCell)methodExecToVertexMap.get(calledMethodExec).getCell(); Point absolutPointSourceVertexCell = getAbsolutePointforCell(sourceVertexCell); // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology. mxgraph.getModel().beginUpdate(); try { mxgraph.removeCells(mxgraph.getEdgesBetween(sourceVertexCell, targetVertexCell)); try { mxICell cloneTargetVertexCell = (mxICell) mxgraph.addCell(targetVertexCell.clone()); Point absolutPointTargetVertexCell = getAbsolutePointforCell(targetVertexCell); cloneTargetVertexCell.getGeometry().setX(absolutPointTargetVertexCell.getX()); cloneTargetVertexCell.getGeometry().setY(absolutPointTargetVertexCell.getY()); cloneTargetVertexCell.setStyle("fillColor=none;strokeColor=none;fontColor=#008000;"); cloneTargetVertexCell.setValue(null); Object tempEdge = mxgraph.insertEdge(mxDefaultParent, null, null, sourceVertexCell, cloneTargetVertexCell); ((mxCell)tempEdge).setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none"); deltaAnimation.setReductionEdgeAnimation(cloneTargetVertexCell, new mxPoint(absolutPointSourceVertexCell.getX(), absolutPointSourceVertexCell.getY() + sourceVertexCell.getGeometry().getHeight())); deltaAnimation.startReductionEdgeAnimation(); // deltaAnimation.setReductionEdgeAnimation(new mxPoint(absolutPointSourceVertexCell.getX() + (sourceVertexCell.getGeometry().getWidth() / 2), absolutPointSourceVertexCell.getY() + sourceVertexCell.getGeometry().getHeight()), new mxPoint(absolutPointTargetVertexCell.getX() + (targetVertexCell.getGeometry().getWidth() / 2), absolutPointTargetVertexCell.getY())); // deltaAnimation.startReductionEdgeAnimation(); mxgraph.removeCells(new Object[]{cloneTargetVertexCell}); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } finally { mxgraph.getModel().endUpdate(); } targetVertexCell.getParent().remove(targetVertexCell); targetVertexCell.setParent(mxDefaultParent); mxgraph.removeCells(new Object[] {targetVertexCell}); objectToVertexMap.get(calledMethodExec.getThisObjId()).getMethodExecutions().remove(methodExecToVertexMap.get(calledMethodExec)); methodExecToVertexMap.get(calledMethodExec).getLocals().remove(sourceVertexObject); methodExecToVertexMap.remove(calledMethodExec); edgeMap.remove(methodExec.getSignature()); // updateVertexObjectSize(); } } /** Make EdgeMethodExecution. */ private void makeEdgeMethodExecution() { List<MethodExecution> methodExecList = new ArrayList<>(methodExecToVertexMap.keySet()); // Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology mxgraph.getModel().beginUpdate(); try { // BUG : Edge Orientation Reverse. for (int i = 0; i < methodExecList.size() - 1; i++) { MethodExecution sourceMethodExec = methodExecList.get(i); MethodExecution targetMethodExec = methodExecList.get(i + 1); String methodSignature = sourceMethodExec.getSignature(); if (!edgeMap.containsKey(methodSignature)) { mxICell sourceVertexCell = (mxICell)methodExecToVertexMap.get(sourceMethodExec).getCell(); mxICell targetVertexCell = (mxICell)methodExecToVertexMap.get(targetMethodExec).getCell(); Point absolutPointSourceVertexCell = getAbsolutePointforCell(sourceVertexCell); Point absolutPointTargetVertexCell = getAbsolutePointforCell(targetVertexCell); // System.out.println("start : " + sourceVertexCell.getGeometry().getCenterX() + ", " + (sourceVertexCell.getGeometry().getY() + sourceVertexCell.getGeometry().getHeight()) + ", " + targetVertexCell.getGeometry().getCenterX() + ", " + targetVertexCell.getGeometry().getY()); // deltaAnimation.setEdgeAnimation(new mxPoint(sourceVertexCell.getGeometry().getCenterX(), sourceVertexCell.getGeometry().getY() + sourceVertexCell.getGeometry().getHeight()), new mxPoint(targetVertexCell.getGeometry().getCenterX(), targetVertexCell.getGeometry().getY())); deltaAnimation.setExpandEdgeAnimation(new mxPoint(absolutPointSourceVertexCell.getX() + (sourceVertexCell.getGeometry().getWidth() / 2), absolutPointSourceVertexCell.getY() + sourceVertexCell.getGeometry().getHeight()), new mxPoint(absolutPointTargetVertexCell.getX() + (targetVertexCell.getGeometry().getWidth() / 2), absolutPointTargetVertexCell.getY())); deltaAnimation.startExpandEdgeAnimation(); targetVertexCell.setVisible(true); Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, methodSignature, null, sourceVertexCell, targetVertexCell); ((mxCell)edge).setStyle("exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;"); edgeMap.put(methodSignature, new Edge(methodSignature, TypeName.Call, edge)); } } } finally { mxgraph.getModel().endUpdate(); } } private int countChildVertex(VertexObject vertexObject) { int time = vertexObject.getMethodExecutions().size(); if(time == 0) { return 1; } for(VertexMethodExecution vertexMethodExecution: vertexObject.getMethodExecutions()) { for(VertexObject vo: vertexMethodExecution.getLocals()) { time += countChildVertex(vo); } for(VertexObject vo: vertexMethodExecution.getArguments()) { return countChildVertex(vo); } } System.out.println(vertexObject.getLabel() + ": " + time); return time; } private String[] formatFieldName(String fieldName) { String fieldNames[] = fieldName.split("\\."); String names[] = new String[] {fieldNames[0], fieldNames[fieldNames.length - 1]}; for(int i = 1; i < fieldNames.length - 1; i++) { names[0] += "." + fieldNames[i]; } return names; } private String formatMethodSignature(String methodSignature) { // Step1 : split "(" String[] methodSignatures = methodSignature.split("\\("); methodSignature = methodSignatures[0]; // Step2 : split " " methodSignatures = methodSignature.split(" "); methodSignature = methodSignatures[1]; methodSignature += "()"; return methodSignature; } }