package org.ntlab.deltaViewer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.awt.geom.Dimension2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.AbstractMap;
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 java.util.concurrent.ThreadPoolExecutor;
import javax.swing.JPanel;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedWeightedPseudograph;
import org.ntlab.animations.MagnetRONAnimation;
import org.ntlab.animations.TranslateAnimation;
import org.ntlab.animations.VertexResizeAnimation;
import org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.IAliasCollector;
import org.ntlab.deltaExtractor.Alias.AliasType;
import org.ntlab.deltaViewer.Edge.TypeName;
import org.ntlab.trace.ArrayUpdate;
import org.ntlab.trace.FieldUpdate;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.MethodInvocation;
import org.ntlab.trace.ObjectReference;
import org.ntlab.trace.Reference;
import org.ntlab.trace.Statement;
import org.ntlab.trace.TracePoint;
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;
/**
*
* @author Nitta Lab.
*/
public abstract class MagnetRONViewer extends JPanel {
private static final long serialVersionUID = -6828987937804142956L;
// Test code (will be deleted)
private static final String TAG = MagnetRONViewer.class.getSimpleName();
protected static final Dimension DEFAULT_COMPONENT_SIZE = new Dimension(1300, 700);
protected static final Dimension DEFAULT_OBJECT_VERTEX_SIZE = new Dimension(70, 70);
protected static final Dimension DEFAULT_METHOD_EXECUTION_VERTEX_SIZE = new Dimension(55, 20);
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 mxGraphComponent mxgraphComponent;
protected DeltaGraphAdapter mxgraph;
protected mxICell defaultParent;
protected int curFrame = 0;
protected int prevFrame = 0;
protected int skipBackFrame = 0; // Use assigned value, when skip back animation.
protected double animationSpeed = DEFAULT_ANIMATION_SPEED;
protected static final double DEFAULT_ANIMATION_SPEED = 1.0;
protected ThreadPoolExecutor threadPoolExecutor;
protected long animationDelayMillis = DEFAULT_ANIMATION_DELAY_MILLIS;
protected static final long DEFAULT_ANIMATION_DELAY_MILLIS = 250L;
protected int magnetRONAnimationTotalCycleCount = DEFAULT_MAGNETRON_ANIMATION_TOTAL_CYCLE_COUNT;
protected static final int DEFAULT_MAGNETRON_ANIMATION_TOTAL_CYCLE_COUNT = 10;
protected long magnetRONAnimationDelayMillis = DEFAULT_MAGNETRON_ANIMATION_DELAY_MILLIS;
protected static final long DEFAULT_MAGNETRON_ANIMATION_DELAY_MILLIS = 100L;
private boolean fSkipBackAnimation = false;
private boolean fAutoTracking = false;
public MagnetRONViewer() {
this.mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph<>(DefaultEdge.class));
this.defaultParent = (mxCell) mxgraph.getDefaultParent();
this.mxgraphComponent = new mxGraphComponent(mxgraph) {
public mxInteractiveCanvas createCanvas() {
return new CurvedCanvas(this);
}
};
mxgraphComponent.setPreferredSize(DEFAULT_COMPONENT_SIZE);
this.setLayout(new BorderLayout());
this.add(mxgraphComponent, BorderLayout.CENTER);
this.threadPoolExecutor = new MagnetRONScheduledThreadPoolExecutor(2);
}
public mxGraphComponent getGraphComponent() {
return mxgraphComponent;
}
protected mxICell getMxDefaultParent() {
return defaultParent;
}
public void clear() {
this.mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph<>(DefaultEdge.class));
this.defaultParent = (mxCell) mxgraph.getDefaultParent();
mxgraphComponent.setGraph(mxgraph);
curFrame = 0;
objectToVertexMap.clear();
methodExecToVertexMap.clear();
edgeMap.clear();
}
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);
/**
* Do animation from fromFrame to toFrame.
*
* @param fromFrame
* @param toFrame
*/
protected void doAnimation(int fromFrame, int toFrame) {
for (int i = fromFrame; i <= toFrame; i++) {
List<Alias> aliasList = new ArrayList<>(aliasCollector.getAliasList());
Alias alias = aliasList.get(i);
// Test code (will be deleted)
System.out.println("\r\n" + TAG + ": Frame=" + i + ", aliasType=" + alias.getAliasType().toString() + ", objectId=" + alias.getObjectId() + ", methodSignature=" + alias.getMethodSignature() + ", l." + alias.getLineNo());
switch(alias.getAliasType()) {
case RETURN_VALUE:
prevFrame = curFrame;
moveObjectVertex(alias);
update();
break;
case METHOD_INVOCATION:
prevFrame = curFrame;
removeMethodExecutionVertex(alias);
moveObjectVertex(alias);
update();
break;
case CONSTRACTOR_INVOCATION:
prevFrame = curFrame;
// TODO: Confirm the program behavior when called after RECEIVER.
if (!objectToVertexMap.containsKey(alias.getObjectId()) || objectToVertexMap.get(alias.getObjectId()).getCell() == null) {
createObjectVertexOnConstractor(alias);
}
if (!methodExecToVertexMap.containsKey(((MethodInvocation)alias.getOccurrencePoint().getStatement()).getCalledMethodExecution())) {
createMethodExecutionVertex(alias.getObjectId(), ((MethodInvocation)alias.getOccurrencePoint().getStatement()).getCallerSideMethodName(), ((MethodInvocation)alias.getOccurrencePoint().getStatement()).getCalledMethodExecution());
update();
}
removeMethodExecutionVertex(alias);
update();
break;
case FORMAL_PARAMETER:
prevFrame = curFrame;
moveObjectVertex(alias);
update();
break;
case ACTUAL_ARGUMENT:
prevFrame = curFrame;
moveObjectVertex(alias);
update();
break;
case THIS:
prevFrame = curFrame;
if (curFrame == 0 || alias.getObjectId().startsWith("0:")) {
createMethodExecutionVertex(alias);
update();
}
break;
case RECEIVER:
// Make {@code MethodExecutionVertex} of called method execution.
MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution();
if (calledMethodExec.isConstructor()
&& (!objectToVertexMap.containsKey(alias.getObjectId()) || objectToVertexMap.get(alias.getObjectId()).getCell() == null)) {
prevFrame = curFrame;
createObjectVertexOnConstractor(alias);
}
if (!methodExecToVertexMap.containsKey(calledMethodExec)) {
MethodExecution methodExec = alias.getMethodExecution();
if (!methodExecToVertexMap.containsKey(methodExec)
&& methodExec.getSignature() != calledMethodExec.getSignature()
&& objectToVertexMap.containsKey(methodExec.getThisObjId())) {
createMethodExecutionVertex(methodExec.getThisObjId(), methodExec.getSignature(), methodExec);
}
prevFrame = curFrame;
createMethodExecutionVertex(alias.getObjectId(), calledMethodExec.getSignature(), calledMethodExec);
update();
}
break;
default:
break;
}
curFrame = i + 1;
}
}
/**
* Create a {@code mxIcell} of {@code ObjectVertex} while animating {@code TranslateAnimation}
* when {@link Alias#getAliasType()} is CONSTRACTOR_INVOCATION.
*
* @param alias
*/
protected void createObjectVertexOnConstractor(Alias alias) {
ObjectVertex objVx = objectToVertexMap.get(alias.getObjectId());
mxICell objVxCell = null;
// Position of srcCell is start point for ovCell to TranslateAnimation.
MethodExecution methodExec = alias.getMethodExecution();
String srcObjId = methodExec.getThisObjId();
if (srcObjId.matches("0")) {
srcObjId += ":" + alias.getMethodExecution().getThisClassName();
}
mxICell srcObjVxCell = (mxICell) objectToVertexMap.get(srcObjId).getCell();
double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
double overlapWid = srcObjVxCellWid * Math.sqrt(2) * 0.1;
double overlapHt = srcObjVxCellHt - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
MethodInvocation methodInv;
String fieldName = null;
if (!methodExec.isCollectionType()
&& alias.getOccurrencePoint().getStatement() != null) {
methodInv = (MethodInvocation) alias.getOccurrencePoint().getStatement();
fieldName = methodInv.getCallerSideMethodName();
}
MagnetRONAnimation.waitAnimationEnd();
scrollCellAndPointToVisible(srcObjVxCell, objVx.getInitialPoint(), 2);
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Creates a white mxICell of ObjectVertex.
objVxCell =
(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), alias.getObjectId(), objVx.getLabel(),
srcObjVxCellAbsPt.getX() + overlapWid, srcObjVxCellAbsPt.getY() + overlapHt,
DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(),
"fillColor=white");
objVx.setCell(objVxCell);
mxICell edgeCell = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcObjVxCell, objVxCell);
edgeMap.put(alias.getObjectId() + "." + fieldName, new Edge(fieldName, TypeName.Create, edgeCell));
for (String edgeKey: edgeMap.keySet()) {
Edge edge = edgeMap.get(edgeKey);
if (edgeKey.startsWith(alias.getObjectId() + ".") && edge.getTypeName() == Edge.TypeName.PreReference) {
mxICell dstCell = (mxICell) objectToVertexMap.get(edge.getDstObjId()).getCell();
String fieldNames[] = edgeKey.split(".");
mxICell edgeCell2 = null;
if (fieldNames.length < 2) {
edgeCell2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), "", "", objVxCell, dstCell);
} else {
edgeCell2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldNames[1], fieldNames[1], objVxCell, dstCell);
}
edge.setTypeName(Edge.TypeName.Reference);
edge.setCell(edgeCell2);
}
}
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
objVxCellAnim.syncPlay();
}
/**
* Create a {@code mxIcell} of {@code ObjectVertex} while animating {@code TranslateAnimation} when {@link Alias#getAliasType()} is CONSTRACTOR_INVOCATION preceded by FORMAL_PARAMETER.
*
* @param alias
*/
protected void createObjectVertexOnConstractorByFormalParameter(Alias alias) {
ObjectVertex objVx = objectToVertexMap.get(alias.getObjectId()); // Create mxICell of this ObjectVertex.
MethodExecution methodExec = alias.getMethodExecution();
mxICell objVxCell = null;
// Position of srcCell is start point for ovCell to TranslateAnimation.
String srcObjId = methodExec.getArguments().get(0).getId();
mxICell srcObjVxCell = (mxICell) objectToVertexMap.get(srcObjId).getCell();
double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
double overlapWidth = srcObjVxCellWid * Math.sqrt(2) * 0.1;
double overlapHeight = srcObjVxCellHt - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
scrollCellAndPointToVisible(srcObjVxCell, objVx.getInitialPoint(), 2);
MethodInvocation methodInv;
String fieldName = null;
if (!methodExec.isCollectionType() && alias.getOccurrencePoint().getStatement() != null) {
methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
fieldName = methodInv.getCallerSideMethodName();
}
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Creates a white mxIcell of ObjectVertex.
objVxCell =
(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), objVx.getLabel(), objVx.getLabel(),
srcObjVxCellAbsPt.getX() + overlapWidth, srcObjVxCellAbsPt.getY() + overlapHeight,
DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(),
"fillColor=white");
objVx.setCell(objVxCell);
mxICell edgeCell = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcObjVxCell, objVxCell);
edgeMap.put(alias.getObjectId() + "." + fieldName, new Edge(fieldName, TypeName.Create, edgeCell));
for (String edgeKey: edgeMap.keySet()) {
Edge edge = edgeMap.get(edgeKey);
if (edgeKey.startsWith(alias.getObjectId() + ".") && edge.getTypeName() == Edge.TypeName.PreReference) {
mxICell dstCell = (mxICell) objectToVertexMap.get(edge.getDstObjId()).getCell();
String fieldNames[] = edgeKey.split(".");
mxICell edgeCell2 = null;
if (fieldNames.length < 2) {
edgeCell2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), "", "", objVxCell, dstCell);
} else {
edgeCell2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldNames[1], fieldNames[1], objVxCell, dstCell);
}
edge.setTypeName(Edge.TypeName.Reference);
edge.setCell(edgeCell2);
}
}
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
objVxCellAnim.syncPlay();
}
/**
* Create edge of {@code ObjectReference}.
*
* @param fieldUpdateStatement
* @param fieldName
*/
protected void createObjectRefrence(FieldUpdate fieldUpdateStatement, String fieldName) {
// Create edge between source ObjectVertex mxICell and destination ObjectVertex mxICell.
String srcObjId = fieldUpdateStatement.getContainerObjId();
mxICell srcObjVxCell = (mxICell) objectToVertexMap.get(srcObjId).getCell();
String dstObjId = fieldUpdateStatement.getValueObjId();
ObjectVertex dstObjVx = objectToVertexMap.get(dstObjId);
mxICell dstObjVxCell = (mxICell) dstObjVx.getCell();
Point2D dstObjVxCellAbsPt = getAbsolutePointforCell(dstObjVxCell);
scrollCellAndPointToVisible(srcObjVxCell, dstObjVx.getInitialPoint(), 2);
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if (!dstObjVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstObjVxCell.getParent().remove(dstObjVxCell);
dstObjVxCell.setParent(getMxDefaultParent());
}
dstObjVxCell.getGeometry().setX(dstObjVxCellAbsPt.getX());
dstObjVxCell.getGeometry().setY(dstObjVxCellAbsPt.getY());
mxICell edgeCell =
(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldUpdateStatement.getFieldName(), fieldName,
srcObjVxCell, dstObjVxCell);
edgeCell.setStyle("strokeColor=red;");
// mxgraph.orderCells(true, new Object[] {edge});
edgeMap.put(fieldUpdateStatement.getContainerObjId(), new Edge(fieldName, TypeName.Reference, edgeCell));
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation dstObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
dstObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
dstObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
dstObjVxCellAnim.init(dstObjVxCell, dstObjVx.getInitialX(), dstObjVx.getInitialY(), threadPoolExecutor);
dstObjVxCellAnim.syncPlay();
// If the animation didn't work to the end.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
dstObjVxCell.getGeometry().setX(dstObjVx.getInitialX());
dstObjVxCell.getGeometry().setY(dstObjVx.getInitialY());
} finally {
mxgraph.getModel().endUpdate();
}
}
}
/**
* Create edge of {@code ObjectReference}.
*
* @param sourceClassName
* @param sourceObjectId
* @param destinationObjectId
*/
protected void createObjectRefrence(String sourceClassName, String sourceObjectId, String destinationObjectId) {
// Create edge between source ObjectVertex mxICell and destination ObjectVertex mxICell.
mxICell srcObjVxCell = (mxICell)objectToVertexMap.get(sourceObjectId).getCell();
ObjectVertex dstObjVx = objectToVertexMap.get(destinationObjectId);
mxICell dstObjVxCell = (mxICell) dstObjVx.getCell();
Point2D dstObjVxCellAbsPt = getAbsolutePointforCell(dstObjVxCell);
scrollCellAndPointToVisible(srcObjVxCell, dstObjVx.getInitialPoint(), 2);
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if (!dstObjVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstObjVxCell.getParent().remove(dstObjVxCell);
dstObjVxCell.setParent(getMxDefaultParent());
}
dstObjVxCell.getGeometry().setX(dstObjVxCellAbsPt.getX());
dstObjVxCell.getGeometry().setY(dstObjVxCellAbsPt.getY());
mxICell edgeCell =
(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), destinationObjectId, null,
srcObjVxCell, dstObjVxCell);
edgeCell.setStyle("strokeColor=red;");
edgeMap.put(destinationObjectId, new Edge(null, TypeName.Reference, edgeCell));
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation dstObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
dstObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
dstObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
dstObjVxCellAnim.init(dstObjVxCell, dstObjVx.getInitialX(), dstObjVx.getInitialY(), threadPoolExecutor);
dstObjVxCellAnim.syncPlay();
// If the animation didn't work to the end.
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
dstObjVxCell.getGeometry().setX(dstObjVx.getInitialX());
dstObjVxCell.getGeometry().setY(dstObjVx.getInitialY());
} finally {
mxgraph.getModel().endUpdate();
}
}
}
/**
* Move to position of destination {@code ObjectVertex} from source {@code ObjectVertex}.
*
* @param alias
*/
protected void moveObjectVertex(Alias alias) {
// source ObjectVertex
String objId = alias.getObjectId();
ObjectVertex srcObjVx = objectToVertexMap.get(objId);
MethodExecution methodExec = alias.getMethodExecution();
if (!methodExecToVertexMap.containsKey(methodExec) && methodExec.isStatic()) {
createMethodExecutionVertex(objId, methodExec.getSignature(), methodExec);
sleepMainThread(getAnimationDelayMillis());
}
// destination ObjectVertex
MethodExecutionVertex dstMethodExecVx = methodExecToVertexMap.get(methodExec);
moveObjectVertex(alias, srcObjVx, dstMethodExecVx);
updateObjectVertices();
}
/**
* Parent: Move to position of destination {@code ObjectVertex} from source {@code ObjectVertex}.
*
* @param alias
* @param sourceVertexObject: source {@code ObjectVertex}
* @param destinationVertexMethodExec: destination {@code MethodExecutionVertex}
*/
private void moveObjectVertex(Alias alias, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
MethodExecution methodExec = alias.getMethodExecution();
AliasType aliasType = alias.getAliasType();
if (aliasType.equals(AliasType.RETURN_VALUE) || aliasType.equals(AliasType.METHOD_INVOCATION)) {
if (sourceObjectVertex.getCell() == null && methodExec.isCollectionType()) {
if (methodExec.getArguments().isEmpty()) {
createObjectVertexOnConstractor(alias);
} else {
createObjectVertexOnConstractorByFormalParameter(alias);
}
}
if (aliasType.equals(AliasType.RETURN_VALUE)) {
MagnetRONAnimation.waitAnimationEnd();
}
moveLocalObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
} else if (aliasType.equals(AliasType.FORMAL_PARAMETER)) {
moveArgumentObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
} else if (aliasType.equals(AliasType.ACTUAL_ARGUMENT)) {
moveActualArgumentObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
}
}
/**
* Move to local position of destination {@code MethodExecutionVertex} from caller {@code MethodExecution} of source {@code ObjectVertex}.
*
* @param callerMethodExecution: caller {@code MethodExecution}
* @param sourceObjectVertex
* @param destinationMethodExecutionVertex
*/
private void moveLocalObjectVertex(MethodExecution callerMethodExecution, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
mxICell srcObjVxCell = (mxICell)sourceObjectVertex.getCell();
mxICell dstMethodExecVxCell = (mxICell) destinationMethodExecutionVertex.getCell();
if (srcObjVxCell.equals(dstMethodExecVxCell.getParent())) {
return;
}
scrollCellsToVisible(dstMethodExecVxCell.getParent(), srcObjVxCell);
// Remove source ObjectVertex from Locals and Arguments of caller MethodExecutionVertex.
if (methodExecToVertexMap.containsKey(callerMethodExecution)) {
MethodExecutionVertex callerMethodExecVx = methodExecToVertexMap.get(callerMethodExecution);
if (callerMethodExecVx.getLocals().contains(sourceObjectVertex)) {
callerMethodExecVx.getLocals().remove(sourceObjectVertex);
}
if (callerMethodExecVx.getArguments().contains(sourceObjectVertex)) {
callerMethodExecVx.getArguments().remove(sourceObjectVertex);
}
}
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
double srcObjVxCellX = srcObjVxCell.getGeometry().getX();
double srcObjVxCellY = srcObjVxCell.getGeometry().getY();
// TODO: Confirm why not .equals(getMxDefaultParent()).
if(srcObjVxCell.getParent().getValue() != null) {
Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
srcObjVxCellX = srcObjVxCellAbsPt.getX();
srcObjVxCellY = srcObjVxCellAbsPt.getY();
srcObjVxCell.getParent().remove(srcObjVxCell);
}
mxgraph.orderCells(true, new Object[] {srcObjVxCell});
if (srcObjVxCell.getParent() == null || !srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
// TODO: Confirm why not need following comment out.
// if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
dstMethodExecVxCell.getParent().insert(srcObjVxCell);
}
Point2D dstObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell.getParent());
srcObjVxCell.getGeometry().setX(srcObjVxCellX - dstObjVxCellAbsPt.getX());
srcObjVxCell.getGeometry().setY(srcObjVxCellY - dstObjVxCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
int dstMethodExecVxLocalsSize = destinationMethodExecutionVertex.getLocals().size();
double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
double dstMethodExecVxCellHt = dstMethodExecVxCell.getGeometry().getHeight();
double srcObjVxCellDstX = dstMethodExecVxCell.getGeometry().getX() - (srcObjVxCellWid / Math.sqrt(2.5)) + (srcObjVxCellWid * dstMethodExecVxLocalsSize);
double srcObjVxCellDstY = dstMethodExecVxCell.getGeometry().getY() + dstMethodExecVxCellHt;
MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstX, srcObjVxCellDstY, threadPoolExecutor);
srcObjVxCellAnim.syncPlay();
// If the animation didn't work to the end.
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if (!srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
srcObjVxCell.getParent().remove(srcObjVxCell);
srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
dstMethodExecVxCell.getParent().insert(srcObjVxCell);
}
srcObjVxCell.getGeometry().setX(srcObjVxCellDstX);
srcObjVxCell.getGeometry().setY(srcObjVxCellDstY);
} finally {
mxgraph.getModel().endUpdate();
}
}
destinationMethodExecutionVertex.getLocals().add(sourceObjectVertex);
}
/**
* Move to position of destination {@code MethodExecutionVertex}'s argument from {@code MethodExecution} of source {@code VertexObject}.
*
* @param methodExecution: {@code MethodExecution}
* @param sourceVertexObject: moving {@code VertexObject}
* @param destinationMethodExecutionVertex
*/
protected void moveArgumentObjectVertex(MethodExecution methodExecution, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
mxICell srcObjVxCell = (mxICell)sourceObjectVertex.getCell();
mxICell dstMethodExecVxCell = (mxICell) destinationMethodExecutionVertex.getCell();
// Remove source VertexObject from Locals and Arguments of MethodExecutionVertex.
MethodExecution callerMethodExec = methodExecution.getCallerMethodExecution();
if (methodExecToVertexMap.containsKey(callerMethodExec)) {
MethodExecutionVertex callerMethodExecVx = methodExecToVertexMap.get(callerMethodExec);
mxICell callerMethodExecVxCell = (mxICell) callerMethodExecVx.getCell();
scrollCellsToVisible(callerMethodExecVxCell.getParent(), dstMethodExecVxCell.getParent(), 2);
if (callerMethodExecVx.getLocals().contains(sourceObjectVertex)) {
callerMethodExecVx.getLocals().remove(sourceObjectVertex);
}
if (callerMethodExecVx.getArguments().contains(sourceObjectVertex)) {
callerMethodExecVx.getArguments().remove(sourceObjectVertex);
}
} else {
scrollCellsToVisible(srcObjVxCell, dstMethodExecVxCell.getParent(), 2);
}
int dstMethodExecVxArgumentsSize = destinationMethodExecutionVertex.getArguments().size();
double srcObjVxCellX = srcObjVxCell.getGeometry().getX();
double srcObjVxCellY = srcObjVxCell.getGeometry().getY();
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if(srcObjVxCell.getParent().getValue() != null) {
Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
srcObjVxCellX = srcObjVxCellAbsPt.getX();
srcObjVxCellY = srcObjVxCellAbsPt.getY();
srcObjVxCell.getParent().remove(srcObjVxCell);
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if (!isParent(dstMethodExecVxCell, srcObjVxCell)) {
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
mxgraph.orderCells(true, new Object[] {srcObjVxCell});
if (srcObjVxCell.getParent() == null || !srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
// TODO: Confirm why not need following comment out.
// if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
dstMethodExecVxCell.getParent().insert(srcObjVxCell);
}
Point2D srcObjVxCellParentAbsPt = getAbsolutePointforCell(srcObjVxCell.getParent());
srcObjVxCell.getGeometry().setX(srcObjVxCellX - srcObjVxCellParentAbsPt.getX());
srcObjVxCell.getGeometry().setY(srcObjVxCellY - srcObjVxCellParentAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
double overlapWid = srcObjVxCellWid - (srcObjVxCellWid * Math.sqrt(2) * 0.1);
double overlapHt = srcObjVxCellHt - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
Point2D srcObjVxCellDstPt =
new Point2D.Double(dstMethodExecVxCell.getGeometry().getX() - overlapWid,
dstMethodExecVxCell.getGeometry().getY() - overlapHt + (srcObjVxCellHt * dstMethodExecVxArgumentsSize));
MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstPt.getX(), srcObjVxCellDstPt.getY(), threadPoolExecutor);
sleepMainThread(getAnimationDelayMillis());
srcObjVxCellAnim.syncPlay();
// If the animation didn't work to the end.
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
srcObjVxCell.getGeometry().setX(srcObjVxCellDstPt.getX());
srcObjVxCell.getGeometry().setY(srcObjVxCellDstPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
} else { // TODO: 仕様上のバグ、ループが発生
// 元の ObjectVertex
Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
mxICell dstMethodExecVxParentCell = dstMethodExecVxCell.getParent();
Point2D dstMethodExecVxParentCellAbsPt = getAbsolutePointforCell(dstMethodExecVxParentCell);
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if ((dstMethodExecVxParentCell != null && dstMethodExecVxParentCell.getParent() != null)
|| srcObjVxCell.getParent() != null
|| !dstMethodExecVxParentCell.getParent().equals(getMxDefaultParent())
|| !srcObjVxCell.getParent().equals(dstMethodExecVxParentCell)) {
srcObjVxCell.remove(dstMethodExecVxParentCell);
dstMethodExecVxParentCell.setParent(getMxDefaultParent());
// TODO: Confirm why not need following comment out.
// dstCell.getParent().remove(dstCell);
// TODO: Confirm why not need following comment out.
// srcCell.getParent().remove(srcCell);
srcObjVxCell.setParent(dstMethodExecVxParentCell);
dstMethodExecVxParentCell.insert(srcObjVxCell);
}
dstMethodExecVxParentCell.getGeometry().setX(dstMethodExecVxParentCellAbsPt.getX());
dstMethodExecVxParentCell.getGeometry().setY(dstMethodExecVxParentCellAbsPt.getY());
srcObjVxCell.getGeometry().setX(srcObjVxCellAbsPt.getX() - dstMethodExecVxParentCellAbsPt.getX());
srcObjVxCell.getGeometry().setY(srcObjVxCellAbsPt.getY() - dstMethodExecVxParentCellAbsPt.getY());
srcObjVxCell.setStyle("opacity=50;shape=ellipse");
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
double overlapWid = srcObjVxCellWid - (srcObjVxCellWid * Math.sqrt(2) * 0.1);
double overlapHt = srcObjVxCellHt - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
double srcObjVxCellDstX = dstMethodExecVxCell.getGeometry().getX() - overlapWid + (srcObjVxCellWid * dstMethodExecVxArgumentsSize);
double srcObjVxCellDstY = dstMethodExecVxCell.getGeometry().getY() - overlapHt + (srcObjVxCellHt * dstMethodExecVxArgumentsSize);
MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstX, srcObjVxCellDstY, threadPoolExecutor);
srcObjVxCellAnim.syncPlay();
// If the animation didn't work to the end.
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
srcObjVxCell.getGeometry().setX(srcObjVxCellDstX);
srcObjVxCell.getGeometry().setY(srcObjVxCellDstY);
} finally {
mxgraph.getModel().endUpdate();
}
}
destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
}
}
/**
*
* @param methodExecution: {@code MethodExecution}
* @param sourceVertexObject: moving {@code VertexObject}
* @param destinationVertexMethodExec
*/
/**
* Move to position of destination {@code MethodExecutionVertex}'s actual argument from {@code MethodExecution} of source {@code VertexObject}.
*
* @param methodExecution: {@code MethodExecution}
* @param sourceObjectVertex
* @param destinationMethodExecutionVertex
*/
private void moveActualArgumentObjectVertex(MethodExecution methodExecution, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
mxICell srcObjVxCell = (mxICell)sourceObjectVertex.getCell();
mxICell dstMethodExecVxCell = (mxICell) destinationMethodExecutionVertex.getCell();
if (srcObjVxCell.equals(dstMethodExecVxCell.getParent())) {
return;
}
// Remove source ObjectVertex from Locals and Arguments of MethodExecutionVertex.
if (methodExecToVertexMap.containsKey(methodExecution)) {
if (methodExecToVertexMap.get(methodExecution).getLocals().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(methodExecution).getLocals().remove(sourceObjectVertex);
}
if (methodExecToVertexMap.get(methodExecution).getArguments().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(methodExecution).getArguments().remove(sourceObjectVertex);
}
}
int dstMethodExecVxLocalsSize = destinationMethodExecutionVertex.getLocals().size();
double srcObjVxCellX = srcObjVxCell.getGeometry().getX();
double srcObjVxCellY = srcObjVxCell.getGeometry().getY();
MagnetRONAnimation.waitAnimationEnd();
scrollCellsToVisible(srcObjVxCell, dstMethodExecVxCell.getParent(), 2);
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if(srcObjVxCell.getParent().getValue() != null) {
Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
srcObjVxCellX = srcObjVxCellAbsPt.getX();
srcObjVxCellY = srcObjVxCellAbsPt.getY();
srcObjVxCell.getParent().remove(srcObjVxCell);
}
if (srcObjVxCell.getParent() == null
|| !srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
// TODO: Confirm why not need following comment out.
// if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
dstMethodExecVxCell.getParent().insert(srcObjVxCell);
}
Point2D srcObjVxCellParentAbsPt = getAbsolutePointforCell(srcObjVxCell.getParent());
srcObjVxCell.getGeometry().setX(srcObjVxCellX - srcObjVxCellParentAbsPt.getX());
srcObjVxCell.getGeometry().setY(srcObjVxCellY - srcObjVxCellParentAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
double dstMethodExecVxCellHt = dstMethodExecVxCell.getGeometry().getHeight();
Point2D srcObjVxCellDstPt = new Point2D.Double(dstMethodExecVxCell.getGeometry().getX() - (srcObjVxCellWid / Math.sqrt(3)) + (srcObjVxCellWid * dstMethodExecVxLocalsSize),
dstMethodExecVxCell.getGeometry().getY() + dstMethodExecVxCellHt);
MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstPt.getX(), srcObjVxCellDstPt.getY(), threadPoolExecutor);
srcObjVxCellAnim.syncPlay();
// If the animation didn't work to the end.
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
srcObjVxCell.getGeometry().setX(srcObjVxCellDstPt.getX());
srcObjVxCell.getGeometry().setY(srcObjVxCellDstPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
}
/**
* Update size and position of all {@code ObjectVertex}.
*/
protected void updateObjectVertices() {
MagnetRONAnimation.waitAnimationEnd();
for (ObjectVertex objVx: objectToVertexMap.values()) {
mxICell objVxCell = (mxICell) objVx.getCell();
if (objVxCell == null) continue;
Dimension2D curDim = new Dimension((int) objVxCell.getGeometry().getWidth(), (int) objVxCell.getGeometry().getHeight());
int sizeScale = 0;
for (int i = 0; i < objVxCell.getChildCount(); i++) {
if (!objVxCell.getChildAt(i).getId().contains("clone")) sizeScale++;
}
if (sizeScale == 0) sizeScale = 1;
Dimension2D dstDim =
new Dimension((int) DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * sizeScale,
(int) DEFAULT_OBJECT_VERTEX_SIZE.getHeight() * sizeScale);
Point2D dstPt = new Point2D.Double(objVxCell.getGeometry().getX(), objVxCell.getGeometry().getY());
if(!curDim.equals(dstDim)) {
// Test code (will be deleted)
System.out.println(TAG + ": Update size of ObjectVertex " + objVxCell.getId() + ". " + curDim.getWidth() + "->" + dstDim.getWidth());
if (!objVxCell.getParent().equals(getMxDefaultParent())
&& (objVxCell.getChildCount() != 0 || (curDim.getWidth() > dstDim.getWidth() && curDim.getHeight() > dstDim.getHeight()))) {
double overlapX = (dstDim.getWidth() - curDim.getWidth()) / 2 / Math.sqrt(2);
double overlapY = (dstDim.getHeight() - curDim.getHeight()) / 2 / Math.sqrt(2);
dstPt.setLocation(objVxCell.getGeometry().getX() - overlapX, objVxCell.getGeometry().getY() + overlapY);
// If two and over ObjectVertex are arranged side by side as an argument or local, shift the Y coordinate of ObjectVertex slightly.
for (MethodExecutionVertex methodExecVertex: methodExecToVertexMap.values()) {
List<ObjectVertex> locals = methodExecVertex.getLocals();
if (locals != null && locals.contains(objVx) && locals.indexOf(objVx) >= 1) {
overlapY = (dstDim.getHeight() - objVxCell.getGeometry().getHeight()) / 2;
dstPt.setLocation(dstPt.getX(), objVxCell.getGeometry().getY() + overlapY);
break;
}
List<ObjectVertex> arguments = methodExecVertex.getArguments();
if (arguments != null && arguments.contains(objVx)) {
dstPt.setLocation(dstPt.getX(), objVxCell.getGeometry().getY() - overlapY);
break;
}
}
}
dstPt.setLocation(dstPt.getX() - (dstDim.getWidth() - curDim.getWidth()) / 2, dstPt.getY() - (dstDim.getHeight() - curDim.getHeight()) / 2);
// Test code (will be deleted)
System.out.println(TAG + ": Translate " + objVxCell.getId() + ". Current point=" + objVxCell.getGeometry().getPoint() + ", Destination Point=" + dstPt);
MagnetRONAnimation objVxCellTransAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellTransAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellTransAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellTransAnim.init(objVxCell, dstPt.getX(), dstPt.getY(), threadPoolExecutor);
objVxCellTransAnim.play();
MagnetRONAnimation objVxCellResizeAnim = new VertexResizeAnimation(mxgraph, getGraphComponent());
objVxCellResizeAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellResizeAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellResizeAnim.init(objVxCell, dstDim.getWidth(), dstDim.getHeight(), threadPoolExecutor);
objVxCellResizeAnim.play();
for (int i = 0; i < objVxCell.getChildCount(); i++) {
mxICell objVxCellChild = objVxCell.getChildAt(i);
double childCellCurX = objVxCellChild.getGeometry().getX();
double childCellCurY = objVxCellChild.getGeometry().getY();
Point2D childDstPt =
new Point2D.Double(childCellCurX + (dstDim.getWidth() - curDim.getWidth()) / 2,
childCellCurY + (dstDim.getHeight() - curDim.getHeight()) / 2);
// Test code (will be deleted)
System.out.println(TAG + ": Translate " + objVxCellChild.getId() + " of " + objVxCell.getId() + ". Current point=" + objVxCellChild.getGeometry().getPoint() + ", Destination Point=" + childDstPt);
MagnetRONAnimation childCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
childCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
childCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
childCellAnim.init(objVxCellChild, childDstPt.getX(), childDstPt.getY(), threadPoolExecutor);
childCellAnim.play();
}
MagnetRONAnimation.waitAnimationEnd();
}
}
}
abstract protected void createMethodExecutionVertex(Alias alias);
/**
* Create {@code MethodExecutionVertex} and Call {@link MagnetRONViewer#createEdgesToMethodExecutions()}.
*
* @param objectId
* @param methodSignature: called or this method signature
* @param methodExec: called or this {@code MethodExecution}
*/
protected void createMethodExecutionVertex(String objectId, String methodSignature, MethodExecution methodExecution) {
if (methodSignature == null) {
methodSignature = methodExecution.getSignature();
}
if (methodSignature.matches(".+\\(.*\\)")) {
methodSignature = formatMethodSignature(methodSignature, methodExecution.getThisClassName());
}
if (methodExecution.isStatic() && !objectId.startsWith("0")) { // Check the object id is a formal parameter's one.
objectId = methodExecution.getThisObjId();
if (objectId.matches("0")) {
objectId += ":" + methodExecution.getThisClassName();
}
}
ObjectVertex parentObjVx = objectToVertexMap.get(objectId);
mxICell parentObjVxCell = (mxICell) parentObjVx.getCell();
double coordX = DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * 0.1;
double coordY = DEFAULT_OBJECT_VERTEX_SIZE.getHeight() * 0.5;
double stdX = coordX;
double stdY = 0;
int methodExecVxSize = parentObjVx.getMethodExecutionVertices().size();
if (methodExecVxSize >= 1) {
mxICell stdCell = (mxICell) parentObjVx.getMethodExecutionVertices().get(0).getCell();
stdX = stdCell.getGeometry().getX();
stdY = stdCell.getGeometry().getY();
methodExecVxSize -= 1;
}
mxICell methodExecVxCell = null;
MethodExecutionVertex methodExecVx = null;
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Creates a white cell of MethodExecutionVertex.
methodExecVxCell = (mxICell) mxgraph.insertDeltaVertex(parentObjVxCell, methodSignature, methodSignature, "fillColor=white");
mxgraph.orderCells(false, new Object[] {methodExecVxCell});
methodExecVx =
new MethodExecutionVertex(methodSignature, methodExecVxCell, stdX, coordY * (methodExecVxSize + 1) + stdY,
DEFAULT_METHOD_EXECUTION_VERTEX_SIZE.getWidth(), DEFAULT_METHOD_EXECUTION_VERTEX_SIZE.getHeight());
methodExecToVertexMap.put(methodExecution, methodExecVx);
if(methodExecToVertexMap.size() > 1) {
// Caution: If synchronized block is split here, {@code cell} is visible instantly until cell#setVisible(false) is executed.
methodExecVxCell.setVisible(false);
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if(methodExecToVertexMap.size() > 1) {
createEdgesToMethodExecutions();
}
parentObjVx.addMethodExecutionVertex(methodExecVx);
update();
}
/**
* Remove {@code MethodExecutionVertex} on {@code {@link Alias#getAliasType()} is {@code AliasType.METHOD_INVOCATION}.
*
* @param alias
*/
private void removeMethodExecutionVertex(Alias alias) {
// source ObjectVertex
ObjectVertex srcObjVx = objectToVertexMap.get(alias.getObjectId());
MethodExecution methodExec = alias.getMethodExecution();
AliasType aliasType = alias.getAliasType();
if(aliasType.equals(AliasType.METHOD_INVOCATION) || aliasType.equals(AliasType.CONSTRACTOR_INVOCATION)) {
MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution();
List<ObjectVertex> arguments = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getArguments());
List<ObjectVertex> locals = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getLocals());
if (arguments.size() != 0) {
for (ObjectVertex objVx: arguments) {
// TODO: Implement equals().
if (objVx != srcObjVx) {
mxICell objVxcell = (mxICell)objVx.getCell();
if (!objVxcell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxcell);
objVxcell.getParent().remove(objVxcell);
objVxcell.setParent(getMxDefaultParent());
objVxcell.getGeometry().setX(objVxCellAbsPt.getX());
objVxcell.getGeometry().setY(objVxCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellAnim.init(objVxcell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
objVxCellAnim.play();
methodExecToVertexMap.get(calledMethodExec).getArguments().remove(objVx);
}
}
}
if (locals.size() != 0) {
for (ObjectVertex objVx: locals) {
if (objVx != srcObjVx) {
mxICell objVxCell = (mxICell) objVx.getCell();
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxCell);
if (!objVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
objVxCell.getParent().remove(objVxCell);
objVxCell.setParent(getMxDefaultParent());
}
objVxCell.getGeometry().setX(objVxCellAbsPt.getX());
objVxCell.getGeometry().setY(objVxCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
objVxCellAnim.play();
}
}
}
}
MagnetRONAnimation.waitAnimationEnd();
if (aliasType.equals(AliasType.CONSTRACTOR_INVOCATION)) {
sleepMainThread(500L);
}
removeCalledMethodExecutionVertex(srcObjVx, methodExec, calledMethodExec);
} else {
removeMethodExecutionVertex(srcObjVx, methodExec);
}
}
/**
* Remove {@code MethodExecutionVertex} on AliasType is {@code AliasType.METHOD_INVOCATION}.
*
* @param sourceObjectVertex
* @param methodExecution
*/
private void removeMethodExecutionVertex(ObjectVertex sourceObjectVertex, MethodExecution methodExecution) {
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Remove source {@code ObjectVertex} from Locals and Arguments of called {@code MethodExecution}'s Vertex.
if (methodExecToVertexMap.containsKey(methodExecution)) {
mxCell dstMethodExecVxCell = (mxCell)methodExecToVertexMap.get(methodExecution).getCell();
if (!dstMethodExecVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstMethodExecVxCell.getParent().remove(dstMethodExecVxCell);
dstMethodExecVxCell.setParent(getMxDefaultParent());
}
mxgraph.removeCells(new Object[] {dstMethodExecVxCell});
objectToVertexMap.get(methodExecution.getThisObjId()).getMethodExecutionVertices().remove(methodExecToVertexMap.get(methodExecution));
methodExecToVertexMap.remove(methodExecution);
edgeMap.remove(methodExecution.getSignature());
updateObjectVertices();
}
} finally {
mxgraph.getModel().endUpdate();
}
}
}
/**
* Remove called {@code MethodExecutionVertex} with alias type {@code AliasType#METHOD_INVOCATION}.
*
* @param sourceVertexObject: source object vertex that a called method execution has temporarily
* @param methodExec: current method execution
* @param calledMethodExec: called method execution
*/
protected void removeCalledMethodExecutionVertex(ObjectVertex sourceObjectVertex, MethodExecution methodExecution, MethodExecution calledMethodExecution) {
MagnetRONAnimation.waitAnimationEnd();
// Remove ObjectVertex other than source ObjectVertex from locals and arguments of called MethodExecutionVertex.
if (methodExecToVertexMap.containsKey(calledMethodExecution)) {
MethodExecutionVertex calledMethodExecVx = methodExecToVertexMap.get(calledMethodExecution);
// TODO: Confirm bug.
List<ObjectVertex> arguments = new ArrayList<>(calledMethodExecVx.getArguments());
if (arguments.size() != 0) {
for (ObjectVertex objVx: arguments) {
if (objVx != sourceObjectVertex) {
mxICell objVxCell = (mxICell)objVx.getCell();
Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxCell);
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if (!objVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
objVxCell.getParent().remove(objVxCell);
objVxCell.setParent(getMxDefaultParent());
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if (!objVxCellAbsPt.equals(objVx.getInitialPoint())) {
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
objVxCell.getGeometry().setX(objVxCellAbsPt.getX());
objVxCell.getGeometry().setY(objVxCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
objVxCellAnim.syncPlay();
}
methodExecToVertexMap.get(calledMethodExecution).getArguments().remove(objVx);
}
}
}
List<ObjectVertex> locals = new ArrayList<>(calledMethodExecVx.getLocals());
if (locals.size() != 0) {
for (ObjectVertex objVx: locals) {
if (objVx != sourceObjectVertex) {
mxICell objVxCell = (mxICell)objVx.getCell();
Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxCell);
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if (!objVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
objVxCell.getParent().remove(objVxCell);
objVxCell.setParent(getMxDefaultParent());
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if (!objVxCellAbsPt.equals(objVx.getInitialPoint())) {
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
objVxCell.getGeometry().setX(objVxCellAbsPt.getX());
objVxCell.getGeometry().setY(objVxCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
objVxCellAnim.syncPlay();
}
methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(objVx);
}
}
}
if (methodExecution == null) {
return;
}
mxICell srcMethodExecVxCell = (mxICell)methodExecToVertexMap.get(methodExecution).getCell();
mxICell dstMethodExecVxCell = (mxICell)calledMethodExecVx.getCell();
scrollCellsToVisible(srcMethodExecVxCell.getParent(), dstMethodExecVxCell.getParent());
try {
Point2D srcMethodExecVxCellAbsPt = null;
Point2D dstMethodExecVxCellAbsPt = null;
final mxICell[] cloneDstMethodExecVxCell = new mxICell[1];
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
mxgraph.removeCells(mxgraph.getEdgesBetween(srcMethodExecVxCell, dstMethodExecVxCell));
srcMethodExecVxCellAbsPt = getAbsolutePointforCell(srcMethodExecVxCell);
dstMethodExecVxCellAbsPt = getAbsolutePointforCell(dstMethodExecVxCell);
cloneDstMethodExecVxCell[0] = (mxICell) mxgraph.addCell(dstMethodExecVxCell.clone());
cloneDstMethodExecVxCell[0].getGeometry().setX(dstMethodExecVxCellAbsPt.getX());
cloneDstMethodExecVxCell[0].getGeometry().setY(dstMethodExecVxCellAbsPt.getY());
cloneDstMethodExecVxCell[0].setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
cloneDstMethodExecVxCell[0].setValue(null);
mxICell tmpEdgeCell = (mxICell) mxgraph.insertEdge(getMxDefaultParent(), null, null, srcMethodExecVxCell, cloneDstMethodExecVxCell[0]);
tmpEdgeCell.setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
} finally {
mxgraph.getModel().endUpdate();
}
}
// Animate an edge to shrink.
MagnetRONAnimation edgeCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
edgeCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
edgeCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
edgeCellAnim.init(cloneDstMethodExecVxCell[0], srcMethodExecVxCellAbsPt.getX(), srcMethodExecVxCellAbsPt.getY() + srcMethodExecVxCell.getGeometry().getHeight(), threadPoolExecutor);
edgeCellAnim.setOnFinished(new ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Test code (will be deleted)
System.out.println(TAG + ": Shrink edge animation action performed.");
mxgraph.removeCells(new Object[]{cloneDstMethodExecVxCell[0]});
// TODO: Confirm execution order.
if (!dstMethodExecVxCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstMethodExecVxCell.getParent().remove(dstMethodExecVxCell);
dstMethodExecVxCell.setParent(getMxDefaultParent());
}
mxgraph.removeCells(new Object[] {dstMethodExecVxCell});
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
}
});
edgeCellAnim.play();
if (!calledMethodExecution.isStatic()) {
objectToVertexMap.get(calledMethodExecution.getThisObjId()).getMethodExecutionVertices().remove(methodExecToVertexMap.get(calledMethodExecution));
} else {
// TODO: Confirm why is this object id of the caller method used?
String objId = calledMethodExecution.getCallerMethodExecution().getThisObjId();
if (objId.matches("0")) {
objId += ":" + calledMethodExecution.getCallerMethodExecution().getThisClassName();
}
objectToVertexMap.get(objId).getMethodExecutionVertices().remove(methodExecToVertexMap.get(calledMethodExecution));
}
methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(sourceObjectVertex);
methodExecToVertexMap.remove(calledMethodExecution);
edgeMap.remove(methodExecution.getSignature());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
sleepMainThread(getAnimationDelayMillis());
}
}
/**
* Create an edge between {@code MethodExecutions} while animating the edge to stretch.
*/
private void createEdgesToMethodExecutions() {
List<MethodExecution> methodExecList = new ArrayList<>(methodExecToVertexMap.keySet());
// TODO: Fix a bug where an edge orientation is reversed.
for (int i = 0; i < methodExecList.size() - 1; i++) {
MethodExecution srcMethodExec = methodExecList.get(i);
MethodExecution dstMethodExec = methodExecList.get(i + 1);
String methodSig = srcMethodExec.getSignature();
if (!edgeMap.containsKey(methodSig)) {
// Draw an edge from sourceVertexCell to destinationVertexCell.
mxICell srcMethodExecVxCell = (mxICell)methodExecToVertexMap.get(srcMethodExec).getCell();
mxICell dstMethodExecVxCell = (mxICell)methodExecToVertexMap.get(dstMethodExec).getCell();
Point2D srcMethodExecVxCellAbsPt = getAbsolutePointforCell(srcMethodExecVxCell);
Point2D dstMethodExecVxCellAbsPt = getAbsolutePointforCell(dstMethodExecVxCell);
MagnetRONAnimation.waitAnimationEnd();
scrollCellsToVisible(srcMethodExecVxCell.getParent(), dstMethodExecVxCell.getParent(), 2);
try {
final mxICell[] cloneDstMethodExecVxCell = new mxICell[1];
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
cloneDstMethodExecVxCell[0] = (mxICell) mxgraph.addCell(dstMethodExecVxCell.clone());
cloneDstMethodExecVxCell[0].getGeometry().setX(srcMethodExecVxCellAbsPt.getX());
cloneDstMethodExecVxCell[0].getGeometry().setY(srcMethodExecVxCellAbsPt.getY() + dstMethodExecVxCell.getGeometry().getHeight());
cloneDstMethodExecVxCell[0].setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
cloneDstMethodExecVxCell[0].setValue(null);
cloneDstMethodExecVxCell[0].setVisible(true);
mxICell tmpEdgeCell = (mxICell) mxgraph.insertEdge(getMxDefaultParent(), null, null, srcMethodExecVxCell, cloneDstMethodExecVxCell[0]);
tmpEdgeCell.setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
dstMethodExecVxCell.setVisible(true);
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
// Animate an edge to stretch.
MagnetRONAnimation edgeCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
edgeCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
edgeCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
edgeCellAnim.init(cloneDstMethodExecVxCell[0], dstMethodExecVxCellAbsPt.getX(), dstMethodExecVxCellAbsPt.getY(), threadPoolExecutor);
edgeCellAnim.setOnFinished(new ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Test code (will be deleted)
System.out.println(TAG + ": Stretch edge animation action performed. ");
mxICell edgeCell = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), methodSig, null, srcMethodExecVxCell, dstMethodExecVxCell);
if (!edgeCell.getParent().equals(getMxDefaultParent())) {
// If parent of Edge cell isn't mxDefaltParent, reset parent.
edgeCell.getParent().remove(edgeCell);
edgeCell.setParent(getMxDefaultParent());
}
mxgraph.orderCells(false, new Object[] {edgeCell});
edgeCell.setStyle("exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;");
edgeMap.put(methodSig, new Edge(methodSig, TypeName.Call, edgeCell));
mxgraph.removeCells(new Object[]{cloneDstMethodExecVxCell[0]});
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
}
});
edgeCellAnim.play();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
}
/**
* Styles all cells on the graph.
*/
protected void setCellsStyle() {
List<Object> objVxs = new ArrayList<>();
List<Object> staticObjVxs = new ArrayList<>();
List<Object> alignMidObjVxs = new ArrayList<>();
List<Object> alignTopObjVxs = new ArrayList<>();
List<Object> refEdges = new ArrayList<>();
List<Object> refCreateEdges = new ArrayList<>();
List<Object> methodExecEdges = new ArrayList<>();
List<Object> roundEdges = new ArrayList<>();
for (Entry<String, ObjectVertex> objectToVertexEntry: objectToVertexMap.entrySet()) {
String objId = objectToVertexEntry.getKey();
ObjectVertex objVx = objectToVertexEntry.getValue();
if (objId.startsWith("0:")) {
staticObjVxs.add(objVx.getCell());
} else {
objVxs.add(objVx.getCell());
}
if(objVx.getMethodExecutionVertices().size() == 0) {
alignMidObjVxs.add(objVx.getCell());
} else {
alignTopObjVxs.add(objVx.getCell());
}
}
List<MethodExecutionVertex> methodExecVxList = new ArrayList<>(methodExecToVertexMap.values());
Collections.reverse(methodExecVxList);
for (int i = 0; i < methodExecVxList.size(); i++) {
mxICell methodExecVxCell = (mxICell) methodExecVxList.get(i).getCell();
if (i == 0) {
methodExecVxCell.setStyle("fillColor=#ff7fbf");
} else if (i == 1) {
methodExecVxCell.setStyle("fillColor=#ff99cc");
} else if (i == 2) {
methodExecVxCell.setStyle("fillColor=#ffb2d8");
} else if (i == 3) {
methodExecVxCell.setStyle("fillColor=#ffcce5");
} else if (i == 4) {
methodExecVxCell.setStyle("fillColor=#ffe0ef");
} else {
break;
}
}
for (Edge edge: edgeMap.values()) {
roundEdges.add(edge.getCell());
switch(edge.getTypeName()) {
case Reference:
refEdges.add(edge.getCell());
break;
case Create:
refEdges.add(edge.getCell());
refCreateEdges.add(edge.getCell());
break;
case Call:
methodExecEdges.add(edge.getCell());
break;
default:
break;
}
}
// Styles ObjectVertex.
mxgraph.setCellStyles(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE, objVxs.toArray(new Object[objVxs.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE, objVxs.toArray(new Object[objVxs.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_UNDERLINE, true, objVxs.toArray(new Object[objVxs.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, alignMidObjVxs.toArray(new Object[alignMidObjVxs.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, alignTopObjVxs.toArray(new Object[alignTopObjVxs.size()]));
// Styles Edge.
mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM, refEdges.toArray(new Object[refEdges.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, refCreateEdges.toArray(new Object[refCreateEdges.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_ROUNDED, 1, true, roundEdges.toArray(new Object[roundEdges.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, roundEdges.toArray(new Object[roundEdges.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_BOTTOM, roundEdges.toArray(new Object[roundEdges.size()]));
// Styles MethodExecutionVertex.
mxgraph.setCellStyles(mxConstants.STYLE_STROKECOLOR, "#008000", methodExecEdges.toArray(new Object[methodExecEdges.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, methodExecEdges.toArray(new Object[methodExecEdges.size()]));
}
protected Point2D getAbsolutePointforCell(mxICell cell) {
Point2D p1 = new Point2D.Double(cell.getGeometry().getX(), cell.getGeometry().getY());;
if(cell.getParent() == null
|| cell.getParent().getValue() == null
|| cell.equals(cell.getParent())) {
return p1;
}
Point2D p2 = getAbsolutePointforCell(cell.getParent());
return new Point2D.Double(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}
/**
* Update the graph on the JFrame by styling the cells and refreshing the mxgraphComponent.
*/
protected void update() {
/* 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! */
setCellsStyle();
getGraphComponent().refresh();
}
protected void scrollCellToVisible(mxICell cell, boolean center) {
if (isAutoTracking()) {
getGraphComponent().scrollCellToVisible(cell, center);
}
}
private void scrollCellsToVisible(mxICell cell1, mxICell cell2) {
scrollCellsToVisible(cell1, cell2, 1);
}
private void scrollCellsToVisible(mxICell cell1, mxICell cell2, int priorityArgumentsIndex) {
if (isAutoTracking()) {
Point2D p1 = getAbsolutePointforCell(cell1);
Point2D p2 = getAbsolutePointforCell(cell2);
if (p1.getX() <= p2.getX()) {
if (p1.getY() <= p2.getY()) {
double p2X = p2.getX() + cell2.getGeometry().getWidth();
double p2Y = p2.getY() + cell2.getGeometry().getHeight();
p2.setLocation(p2X, p2Y);
} else {
double p1Y = p1.getY() + cell1.getGeometry().getHeight();
double p2X = p2.getX() + cell2.getGeometry().getWidth();
p1.setLocation(p1.getX(), p1Y);
p2.setLocation(p2X, p2.getY());
}
} else {
if (p1.getY() <= p2.getY()) {
double p1X = p1.getX() + cell1.getGeometry().getWidth();
double p2Y = p2.getY() + cell2.getGeometry().getHeight();
p1.setLocation(p1X, p1.getY());
p2.setLocation(p2.getX(), p2Y);
} else {
double p1X = p1.getX() + cell1.getGeometry().getWidth();
double p1Y = p1.getY() + cell1.getGeometry().getHeight();
p1.setLocation(p1X, p1Y);
}
}
scrollPointsToVisible(p1, p2, false, priorityArgumentsIndex);
}
}
private void scrollCellAndPointToVisible(mxICell cell1, Point2D p2, int priorityArgumentsIndex) {
if (isAutoTracking()) {
Point2D p1 = getAbsolutePointforCell(cell1);
if (p1.getX() <= p2.getX()) {
if (p1.getY() <= p2.getY()) {
double p2X = p2.getX() + DEFAULT_OBJECT_VERTEX_SIZE.getWidth();
double p2Y = p2.getY() + DEFAULT_OBJECT_VERTEX_SIZE.getHeight();
p2.setLocation(p2X, p2Y);
} else {
double p1Y = p1.getY() + cell1.getGeometry().getHeight();
double p2X = p2.getX() + DEFAULT_OBJECT_VERTEX_SIZE.getWidth();
p1.setLocation(p1.getX(), p1Y);
p2.setLocation(p2X, p2.getY());
}
} else {
if (p1.getY() <= p2.getY()) {
double p1X = p1.getX() + cell1.getGeometry().getWidth();
double p2Y = p2.getY() + DEFAULT_OBJECT_VERTEX_SIZE.getHeight();
p1.setLocation(p1X, p1.getY());
p2.setLocation(p2.getX(), p2Y);
} else {
double p1X = p1.getX() + cell1.getGeometry().getWidth();
double p1Y = p1.getY() + cell1.getGeometry().getHeight();
p1.setLocation(p1X, p1Y);
}
}
scrollPointsToVisible(p1, p2, false, priorityArgumentsIndex);
}
}
/**
*
*
* @param p1
* @param p2
* @param center
* @param priorityArgumentsIndex: 1 is the default value, indicating that 1st argument(p1) of this method should be preferred.
*/
private void scrollPointsToVisible(Point2D p1, Point2D p2, boolean center, int priorityArgumentsIndex) {
if (isAutoTracking()) {
Rectangle rec = new Rectangle();
int p1X = (int) p1.getX(), p1Y = (int) p1.getY();
int p2X = (int) p2.getX(), p2Y = (int) p2.getY();
if (p1X <= p2X) {
if (p1Y <= p2Y) {
rec.setBounds(p1X, p1Y, p2X - p1X, p2Y - p1Y);
if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
switch(priorityArgumentsIndex) {
case 1:
break;
case 2:
int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
rec.setLocation(x, y);
break;
}
rec.setSize(super.getWidth(), super.getHeight());
}
} else {
rec.setBounds(p1X, p2Y, p2X - p1X, p1Y - p2Y);
if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
switch(priorityArgumentsIndex) {
case 1:
int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
rec.setLocation((int) rec.getX(), y);
break;
case 2:
int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
rec.setLocation(x, (int) rec.getY());
break;
}
rec.setSize(super.getWidth(), super.getHeight());
}
}
} else {
if (p1Y <= p2Y) {
rec.setBounds(p2X, p1Y, p1X - p2X, p2Y - p1Y);
if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
switch(priorityArgumentsIndex) {
case 1:
int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
rec.setLocation(x, (int) rec.getY());
break;
case 2:
int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
rec.setLocation((int) rec.getX(), y);
break;
}
rec.setSize(super.getWidth(), super.getHeight());
}
} else {
rec.setBounds(p2X, p2Y, p1X - p2X, p1Y - p2Y);
if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
switch(priorityArgumentsIndex) {
case 1:
int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
rec.setLocation(x, y);
break;
case 2:
break;
}
rec.setSize(super.getWidth(), super.getHeight());
}
}
}
if (center) {
int x = (int) (rec.getCenterX() - super.getWidth() / 2);
int y = (int) (rec.getCenterY() - super.getHeight() / 2);
rec.setBounds(x, y, super.getWidth(), super.getHeight());
}
scrollRectToVisible(rec);
}
}
@Override
public void scrollRectToVisible(Rectangle rec) {
Rectangle visibleRec = getGraphComponent().getGraphControl().getVisibleRect();
if (isAutoTracking() && !rec.contains(visibleRec)) {
System.out.println(TAG + ": Before scroll visibleRect=" + getGraphComponent().getGraphControl().getVisibleRect());
getGraphComponent().getGraphControl().scrollRectToVisible(rec);
System.out.println(TAG + ": After scroll visibleRect=" + getGraphComponent().getGraphControl().getVisibleRect());
}
}
public boolean isSkipBackAnimation() {
return this.fSkipBackAnimation;
}
private boolean isAutoTracking() {
return this.fAutoTracking;
}
public void setSkipBackAnimation(boolean fSkipBackAnimation) {
this.fSkipBackAnimation = fSkipBackAnimation;
}
public void setAutoTracking(boolean fAutoTracking) {
if (fAutoTracking != isAutoTracking()) {
this.fAutoTracking = fAutoTracking;
}
}
protected void setCurrentFrame(int numberFrame) {
this.curFrame = numberFrame;
}
protected void setSkipBackFrame(int numberFrame) {
this.skipBackFrame = numberFrame;
}
public void setAnimationSpeed(double animationSpeed) {
this.animationSpeed = animationSpeed;
}
protected int getCurrentFrame() {
return this.curFrame;
}
protected int getPreviousFrame() {
return this.prevFrame;
}
public int getSkipBackFrame() {
return this.skipBackFrame;
}
public double getAnimationSpeed() {
return this.animationSpeed;
}
protected long getAnimationDelayMillis() {
return (long) (this.animationDelayMillis / getAnimationSpeed());
}
protected int getMagnetRONAnimationTotalCycleCount() {
return (int) (this.magnetRONAnimationTotalCycleCount / getAnimationSpeed());
}
protected long getMagnetRONAnimationDelayMillis() {
return (long) (this.magnetRONAnimationDelayMillis / getAnimationSpeed());
}
protected static 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;
}
protected String formatMethodSignature(String methodSignature, String thisClassName) {
// TODO: Modify algorithm formatMethodSignature().
// Step1 : split "("
methodSignature = methodSignature.substring(0, methodSignature.lastIndexOf('('));
// Step2 : split " "
String[] methodSigs = methodSignature.split(" ");
String tmpMethodSig = methodSigs[methodSigs.length-1];
// Step3 : split "."
String[] thisClassNames = thisClassName.split("\\.");
methodSigs = tmpMethodSig.split("\\.");
StringBuffer sb = new StringBuffer();
int i = methodSigs.length - 2;
int count = methodSignature.split("\\(").length - 1;
if (count > 0) i -= count;
if (i >= 0 && !thisClassNames[thisClassNames.length - 1].equals(methodSigs[i])) {
if (thisClassNames[thisClassNames.length - 1].equals(methodSigs[i + 1])) i += 1;
sb.append(methodSigs[i]);
if (methodSigs.length - i > 1) sb.append(".");
}
for (i = i + 1; i < methodSigs.length; i++) {
sb.append(methodSigs[i]);
if (methodSigs.length - i > 1) sb.append(".");
}
sb.append("()");
String newMethodSignature = sb.toString();
if (!newMethodSignature.isEmpty()) {
return newMethodSignature;
}
return methodSignature;
}
protected String formatArrayName(String srcClassName) {
// Step1 : remove "[L"
StringBuffer sb = new StringBuffer();
sb.append(srcClassName.substring(2, srcClassName.length()-1));
sb.append("[]");
return sb.toString();
}
protected String formatArrayIndex(int index) {
StringBuffer sb = new StringBuffer();
sb.append("[");
sb.append(index);
sb.append("]");
return sb.toString();
}
/**
* Test code (will be deleted)
*/
protected void outputLog() {
for (Object obj: mxgraph.getChildCells(getMxDefaultParent())) {
System.out.println(obj + " " + obj.hashCode());
for (int i = 0; i < ((mxICell)obj).getChildCount(); i++) {
System.out.println(" " + ((mxICell)obj).getChildAt(i) + " " + obj.hashCode());
}
}
System.out.println("\nObject");
for (Entry<String, ObjectVertex> e: objectToVertexMap.entrySet()) {
String objId = e.getKey();
ObjectVertex vo = e.getValue();
if (vo.getCell() != null) {
System.out.println(vo.getLabel() + " (" + objId + ")" + " " + vo.getCell().hashCode());
} else {
System.out.println(vo.getLabel() + " (" + objId + ")");
}
for (MethodExecutionVertex vme: vo.getMethodExecutionVertices()) {
System.out.println(" " + vme.getLabel());
for (ObjectVertex vmevo: vme.getArguments()) {
System.out.println(" Argument: " + vmevo.getLabel());
}
for (ObjectVertex vmevo: vme.getLocals()) {
System.out.println(" Local: " + vmevo.getLabel());
}
}
}
System.out.println("\nEdge");
for (Edge e: edgeMap.values()) {
System.out.println(e.getLabel() + "(" + ((mxICell)e.getCell()).getId() + ")");
if (((mxICell)e.getCell()).getParent() != null) {
System.out.println(" " + ((mxICell)e.getCell()).getParent().getId());
}
}
}
/**
* Whether parents of source mxICell contain destination mxICell.
*
* @param sourceCell
* @param destinationCell
* @return
*/
private boolean isParent(mxICell sourceCell, mxICell destinationCell) {
mxICell srcParentCell = sourceCell.getParent();
if (srcParentCell == null || srcParentCell.getValue() == null || destinationCell == null) {
return false;
}
if (srcParentCell.equals(destinationCell)) {
return true;
}
return isParent(srcParentCell, destinationCell);
}
public static Map.Entry<Reference, String> getRelatedInformation(TracePoint relatedPoint, IAliasCollector ac) {
Statement rpStatement = relatedPoint.getStatement();
String rpSrcObjId = null;
String rpDstObjId = null;
String rpSrcClassName = null;
String rpDstClassName = null;
String rpFieldName = null;
// Search for relatedPoint objectReference srcClassName, fieldName.
if (relatedPoint.isMethodEntry()) {
// this to another (parameter)
Alias lastAlias = ac.getAliasList().get(ac.getAliasList().size() - 1);
if (lastAlias.getAliasType() == Alias.AliasType.FORMAL_PARAMETER) {
rpSrcObjId = relatedPoint.getMethodExecution().getThisObjId();
rpDstObjId = lastAlias.getObjectId();
rpFieldName = "";
rpSrcClassName = relatedPoint.getMethodExecution().getThisClassName();
for (ObjectReference r: lastAlias.getMethodExecution().getArguments()) {
if (r.getId().equals(rpDstObjId)) {
rpDstClassName = r.getActualType();
break;
}
}
}
}
if (rpSrcObjId == null || rpDstObjId == null) {
if (rpStatement instanceof FieldUpdate) {
// Format fieldName.
FieldUpdate rpFieldUpdateStatement = (FieldUpdate) rpStatement;
rpSrcObjId = rpFieldUpdateStatement.getContainerObjId();
rpDstObjId = rpFieldUpdateStatement.getValueObjId();
if (rpFieldUpdateStatement.getFieldName() != null) {
String rpFieldNames[] = formatFieldName(rpFieldUpdateStatement.getFieldName());
rpSrcClassName = rpFieldNames[0];
rpFieldName = rpFieldNames[rpFieldNames.length-1];
} else {
rpSrcClassName = rpFieldUpdateStatement.getContainerClassName();
rpFieldName = "";
}
rpDstClassName = rpFieldUpdateStatement.getValueClassName();
} else if (rpStatement instanceof ArrayUpdate) {
// container to component
ArrayUpdate rpArrayUpdateStatement = (ArrayUpdate) rpStatement;
rpSrcObjId = rpArrayUpdateStatement.getArrayObjectId();
rpDstObjId = rpArrayUpdateStatement.getValueObjectId();
rpSrcClassName = rpArrayUpdateStatement.getArrayClassName();
rpDstClassName = rpArrayUpdateStatement.getValueClassName();
rpFieldName = "[" + rpArrayUpdateStatement.getIndex() + "]";
} else if(rpStatement instanceof MethodInvocation) {
MethodInvocation rpMethodInvStatement = (MethodInvocation) rpStatement;
MethodExecution rpCalledMethodExec = rpMethodInvStatement.getCalledMethodExecution();
String rpMethodSig = rpCalledMethodExec.getSignature();
//ArrayやListのときだけラベルを付ける(確実に分かっているものとき)getSignature->contains("List.get(") || "Map.get(") <ホワイトリスト>
// if (rpMethodExec.getSignature().contains("List.add(") ||
// rpMethodExec.getSignature().contains("Map.put(")) {
if (rpCalledMethodExec.isCollectionType()
&& (rpMethodSig.contains("add(")
|| rpMethodSig.contains("set(")
|| rpMethodSig.contains("put(")
|| rpMethodSig.contains("push(")
|| rpMethodSig.contains("addElement("))) {
rpSrcClassName = rpCalledMethodExec.getThisClassName();
rpDstClassName = rpCalledMethodExec.getArguments().get(0).getActualType();
rpSrcObjId = rpCalledMethodExec.getThisObjId();
rpDstObjId = rpCalledMethodExec.getArguments().get(0).getId();
} else {
// this to another
rpSrcClassName = rpMethodInvStatement.getThisClassName();
rpDstClassName = rpCalledMethodExec.getReturnValue().getActualType();
rpSrcObjId = rpMethodInvStatement.getThisObjId();
rpDstObjId = rpCalledMethodExec.getReturnValue().getId();
}
}
}
return new AbstractMap.SimpleEntry<>(new Reference(rpSrcObjId, rpDstObjId, rpSrcClassName, rpDstClassName), rpFieldName);
}
public void sleepMainThread(long millis) {
try {
// Test code (will be deleted)
System.out.println(TAG + ": Sleep Main thread " + millis + "millis. ThreadId=" + Thread.currentThread().getId());
Thread.sleep(millis);
System.out.println(TAG + ": Resume Main thread.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
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);
}
}
}
}