package org.ntlab.deltaViewer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Dimension2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
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 Dimension DEFAULT_WINDOW_SIZE = new Dimension(1300, 700);
protected static String WINDOW_TITLE = "Delta Viewer";
protected static Dimension DEFAULT_OBJECT_VERTEX_SIZE = new Dimension(70, 70);
protected static Dimension DEFAULT_METHOD_EXECUTION_VERTEX_SIZE = new Dimension(55, 20);
protected static long DEFAULT_THREAD_SLEEP_MILLIS = 1100L;
protected static long POSTPONE_ANIMATION_MILLIS = 250L;
protected IAliasCollector aliasCollector;
protected Map<String, ObjectVertex> objectToVertexMap = new HashMap<>();
protected Map<MethodExecution, MethodExecutionVertex> methodExecToVertexMap = new LinkedHashMap<>();
protected Map<String, Edge> edgeMap = new HashMap<>();
protected DeltaGraphAdapter mxgraph;
protected mxICell mxDefaultParent;
protected mxGraphComponent mxgraphComponent;
protected ThreadPoolExecutor threadPoolExecutor;
protected DeltaAnimation deltaAnimation;
protected int curFrame = 0;
public MagnetRONViewer() {
mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph(DefaultEdge.class));
mxDefaultParent = (mxCell)mxgraph.getDefaultParent();
mxgraphComponent = new mxGraphComponent(mxgraph) {
public mxInteractiveCanvas createCanvas() {
return new CurvedCanvas(this);
}
};
deltaAnimation = new DeltaAnimation(mxgraph, getGraphComponent());
threadPoolExecutor = new MagnetRONScheduledThreadPoolExecutor(2);
getGraphComponent().setPreferredSize(DEFAULT_WINDOW_SIZE);
setLayout(new BorderLayout());
add(getGraphComponent(), BorderLayout.CENTER);
}
public mxICell getMxDefaultParent() {
return mxDefaultParent;
}
public mxGraphComponent getGraphComponent() {
return mxgraphComponent;
}
public void clear() {
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
for (ObjectVertex ov: objectToVertexMap.values()) {
mxICell ovCell = (mxICell)ov.getCell();
if (ovCell != null) {
if (!ovCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
ovCell.getParent().remove(ovCell);
ovCell.setParent(getMxDefaultParent());
}
}
}
mxgraph.removeCells(mxgraph.getChildVertices(getMxDefaultParent()));
} finally {
mxgraph.getModel().endUpdate();
}
}
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:
moveObjectVertex(alias);
update();
break;
case METHOD_INVOCATION:
removeMethodExecutionVertex(alias);
moveObjectVertex(alias);
update();
break;
case CONSTRACTOR_INVOCATION:
// 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:
moveObjectVertex(alias);
update();
break;
case ACTUAL_ARGUMENT:
moveObjectVertex(alias);
update();
break;
case THIS:
if (curFrame == 0 || alias.getObjectId().matches("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)) {
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);
}
createMethodExecutionVertex(alias.getObjectId(), calledMethodExec.getSignature(), calledMethodExec);
update();
}
break;
default:
break;
}
curFrame = i + 1;
}
}
/**
* Create {@code ObjectVertex} when CONSTRACTOR_INVOCATION.
*
* @param alias
*/
protected void createObjectVertexOnConstractor(Alias alias) {
ObjectVertex objectVertex = objectToVertexMap.get(alias.getObjectId());
MethodExecution methodExec = alias.getMethodExecution();
String srcObjId = alias.getMethodExecution().getThisObjId();
mxICell srcCell = (mxICell)objectToVertexMap.get(srcObjId).getCell();
double srcWidth = srcCell.getGeometry().getWidth();
double srcHeight = srcCell.getGeometry().getHeight();
double overlapWidth = srcWidth * Math.sqrt(2) * 0.1;
double overlapHeight = srcHeight - (srcHeight * Math.sqrt(2) * 0.1);
Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
MethodInvocation methodInv;
String fieldName = null;
if (!methodExec.isCollectionType()
&& alias.getOccurrencePoint().getStatement() != null) {
methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
fieldName = methodInv.getCallerSideMethodName();
}
mxICell ovCell;
MagnetRONAnimation.waitAnimationEnd();
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Creates a white cell of {@code ObjectVertex}.
ovCell =
(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), alias.getObjectId(), objectVertex.getLabel(),
srcCellAbsPt.getX() + overlapWidth, srcCellAbsPt.getY() + overlapHeight,
DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(),
"fillColor=white");
objectVertex.setCell(ovCell);
mxICell edge = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcCell, ovCell);
// setEdgePoint((mxCell)edge, srcCellAbsPt, objectVertex.getInitialPoint());
edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
// setCellsStyle();
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(ovCell, objectVertex.getX(), objectVertex.getY(), threadPoolExecutor);
vertexAnim.syncPlay();
}
/**
* Create {@code ObjectVertex} when CONSTRACTOR_INVOCATION is preceded by FORMAL_PARAMETER.
*
* @param alias
*/
protected void createObjectVertexOnConstractorByFormalParameter(Alias alias) {
ObjectVertex objectVertex = objectToVertexMap.get(alias.getObjectId()); // Create cell of this object vertex.
MethodExecution methodExec = alias.getMethodExecution();
String srcObjId = methodExec.getArguments().get(0).getId();
mxICell srcCell = (mxICell)objectToVertexMap.get(srcObjId).getCell();
double srcWidth = srcCell.getGeometry().getWidth();
double srcHeight = srcCell.getGeometry().getHeight();
double overlapWidth = srcWidth * Math.sqrt(2) * 0.1;
double overlapHeight = srcHeight - (srcHeight * Math.sqrt(2) * 0.1);
Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
MethodInvocation methodInv;
String fieldName = null;
if (!methodExec.isCollectionType() && alias.getOccurrencePoint().getStatement() != null) {
methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
fieldName = methodInv.getCallerSideMethodName();
}
mxICell ovCell;
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
// Creates a white cell of {@code ObjectVertex}.
ovCell =
(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), objectVertex.getLabel(), objectVertex.getLabel(),
srcCellAbsPt.getX() + overlapWidth, srcCellAbsPt.getY() + overlapHeight,
DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(),
"fillColor=white");
objectVertex.setCell(ovCell);
mxICell edge = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcCell, ovCell);
setEdgePoint(edge, srcCellAbsPt, objectVertex.getInitialPoint());
edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
// setCellsStyle();
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(ovCell, objectVertex.getInitialX(), objectVertex.getInitialY(), threadPoolExecutor);
vertexAnim.syncPlay();
}
/**
*
* @param fieldUpdateStatement
* @param fieldName
*/
protected void createObjectRefrence(FieldUpdate fieldUpdateStatement, String fieldName) {
String srcObjId = fieldUpdateStatement.getContainerObjId();
String dstObjId = fieldUpdateStatement.getValueObjId();
ObjectVertex dstObjVertex = objectToVertexMap.get(dstObjId);
mxICell dstCell = (mxICell)dstObjVertex.getCell();
Point2D dstCellAbsPt = getAbsolutePointforCell(dstCell);
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
if (!dstCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstCell.getParent().remove(dstCell);
dstCell.setParent(getMxDefaultParent());
}
dstCell.getGeometry().setX(dstCellAbsPt.getX());
dstCell.getGeometry().setY(dstCellAbsPt.getY());
mxICell edge =
(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldUpdateStatement.getFieldName(), fieldName,
objectToVertexMap.get(srcObjId).getCell(), objectToVertexMap.get(dstObjId).getCell());
edge.setStyle("strokeColor=red;");
// mxgraph.orderCells(true, new Object[] {edge});
edgeMap.put(fieldUpdateStatement.getFieldName(), new Edge(fieldName, TypeName.Reference, edge));
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(dstCell, dstObjVertex.getInitialX(), dstObjVertex.getInitialY(), threadPoolExecutor);
vertexAnim.syncPlay();
mxgraph.getModel().beginUpdate();
synchronized (mxgraph.getModel()) {
try {
dstCell.getGeometry().setX(dstObjVertex.getInitialX());
dstCell.getGeometry().setY(dstObjVertex.getInitialY());
} finally {
mxgraph.getModel().endUpdate();
}
}
}
/**
*
* @param sourceClassName
* @param sourceObjectId
* @param destinationObjectId
*/
protected void createObjectRefrence(String sourceClassName, String sourceObjectId, String destinationObjectId) {
ObjectVertex dstObjVertex = objectToVertexMap.get(destinationObjectId);
mxICell dstCell = (mxICell)dstObjVertex.getCell();
Point2D dstCellAbsPt = getAbsolutePointforCell(dstCell);
// 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 (!dstCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstCell.getParent().remove(dstCell);
dstCell.setParent(getMxDefaultParent());
}
dstCell.getGeometry().setX(dstCellAbsPt.getX());
dstCell.getGeometry().setY(dstCellAbsPt.getY());
mxICell edge =
(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), destinationObjectId, null,
objectToVertexMap.get(sourceObjectId).getCell(), objectToVertexMap.get(destinationObjectId).getCell());
edge.setStyle("strokeColor=red;");
edgeMap.put(destinationObjectId, new Edge(null, TypeName.Reference, edge));
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(dstCell, dstObjVertex.getInitialX(), dstObjVertex.getInitialY(), threadPoolExecutor);
vertexAnim.syncPlay();
// 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 {
dstCell.getGeometry().setX(dstObjVertex.getInitialX());
dstCell.getGeometry().setY(dstObjVertex.getInitialY());
} finally {
mxgraph.getModel().endUpdate();
}
}
}
/**
* Move to position of destination {@code ObjectVertex} from source {@code ObjectVertex}.
*
* @param alias
*/
protected void moveObjectVertex(Alias alias) {
// source {@code ObjectVertex}
ObjectVertex srcObjVertex = objectToVertexMap.get(alias.getObjectId());
if (alias.getMethodExecution().isStatic() && !methodExecToVertexMap.containsKey(alias.getMethodExecution())) {
createMethodExecutionVertex(alias.getObjectId(), alias.getMethodExecution().getSignature(), alias.getMethodExecution());
sleepMainThread(POSTPONE_ANIMATION_MILLIS);
}
// destination {@code ObjectVertex}
MethodExecutionVertex dstMethodExecVertex = methodExecToVertexMap.get(alias.getMethodExecution());
moveObjectVertex(alias, srcObjVertex, dstMethodExecVertex);
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();
if (alias.getAliasType().equals(AliasType.RETURN_VALUE) || alias.getAliasType().equals(AliasType.METHOD_INVOCATION)) {
if (sourceObjectVertex.getCell() == null && methodExec.isCollectionType()) {
if (methodExec.getArguments().isEmpty()) {
createObjectVertexOnConstractor(alias);
} else {
createObjectVertexOnConstractorByFormalParameter(alias);
}
}
if (alias.getAliasType().equals(AliasType.RETURN_VALUE)) {
MagnetRONAnimation.waitAnimationEnd();
}
moveLocalObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
} else if (alias.getAliasType().equals(AliasType.FORMAL_PARAMETER)) {
moveArgumentObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
} else if (alias.getAliasType().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 srcCell = (mxICell)sourceObjectVertex.getCell();
mxICell dstCell = (mxICell) destinationMethodExecutionVertex.getCell();
if (srcCell.equals(dstCell.getParent())) {
return;
}
// Remove sourceObjectVertex from Locals and Arguments of MethodExecution's Vertex.
if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getLocals().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(callerMethodExecution).getLocals().remove(sourceObjectVertex);
}
if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getArguments().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(callerMethodExecution).getArguments().remove(sourceObjectVertex);
}
int time = destinationMethodExecutionVertex.getLocals().size();
// 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 srcCellCoordX = srcCell.getGeometry().getX();
double srcCellCoordY = srcCell.getGeometry().getY();
if(srcCell.getParent().getValue() != null) {
Point2D absolutePointSourceCell = getAbsolutePointforCell(srcCell);
srcCellCoordX = absolutePointSourceCell.getX();
srcCellCoordY = absolutePointSourceCell.getY();
srcCell.getParent().remove(srcCell);
}
mxgraph.orderCells(true, new Object[] {srcCell});
if (srcCell.getParent() == null || !srcCell.getParent().equals(dstCell.getParent())) {
// TODO: Confirm why not need following comment out.
// if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
srcCell.setParent(dstCell.getParent());
dstCell.getParent().insert(srcCell);
}
Point2D dstCellAbsPt = getAbsolutePointforCell(srcCell.getParent());
srcCell.getGeometry().setX(srcCellCoordX - dstCellAbsPt.getX());
srcCell.getGeometry().setY(srcCellCoordY - dstCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcCellWidth = srcCell.getGeometry().getWidth();
double dstCellHeight = dstCell.getGeometry().getHeight();
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(srcCell,
dstCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(2.5)) + (srcCellWidth * time), dstCell.getGeometry().getY() + dstCellHeight,
threadPoolExecutor);
vertexAnim.syncPlay();
// 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 (!srcCell.getParent().equals(dstCell.getParent())) {
srcCell.getParent().remove(srcCell);
srcCell.setParent(dstCell.getParent());
dstCell.getParent().insert(srcCell);
}
srcCell.getGeometry().setX(dstCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(2.5)) + (srcCellWidth * time));
srcCell.getGeometry().setY(dstCell.getGeometry().getY() + dstCellHeight);
destinationMethodExecutionVertex.getLocals().add(sourceObjectVertex);
} finally {
mxgraph.getModel().endUpdate();
}
}
}
/**
* 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 srcCell = (mxICell)sourceObjectVertex.getCell();
mxICell dstCell = (mxICell) destinationMethodExecutionVertex.getCell();
// Remove source {@code VertexObject} from Locals and Arguments of {@code MethodExecution}'s Vertex.
MethodExecution callerMethodExec = methodExecution.getCallerMethodExecution();
if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getLocals().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(callerMethodExec).getLocals().remove(sourceObjectVertex);
}
if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getArguments().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(callerMethodExec).getArguments().remove(sourceObjectVertex);
}
int time = destinationMethodExecutionVertex.getArguments().size();
double srcCoordX = srcCell.getGeometry().getX();
double srcCoordY = srcCell.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(srcCell.getParent().getValue() != null) {
Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
srcCoordX = srcCellAbsPt.getX();
srcCoordY = srcCellAbsPt.getY();
srcCell.getParent().remove(srcCell);
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if (!isParent(dstCell, srcCell)) {
// 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[] {srcCell});
if (srcCell.getParent() == null || !srcCell.getParent().equals(dstCell.getParent())) {
// TODO: Confirm why not need following comment out.
// if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
srcCell.setParent(dstCell.getParent());
dstCell.getParent().insert(srcCell);
}
Point2D srcParentCellAbsPt = getAbsolutePointforCell(srcCell.getParent());
srcCell.getGeometry().setX(srcCoordX - srcParentCellAbsPt.getX());
srcCell.getGeometry().setY(srcCoordY - srcParentCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcCellWidth = srcCell.getGeometry().getWidth();
double srcCellHeight = srcCell.getGeometry().getHeight();
double overlapWidth = srcCellWidth - (srcCellWidth * Math.sqrt(2) * 0.1);
double overlapHeight = srcCellHeight - (srcCellHeight * Math.sqrt(2) * 0.1);
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(srcCell,
dstCell.getGeometry().getX() - overlapWidth, dstCell.getGeometry().getY() - overlapHeight + (srcCellHeight * time),
threadPoolExecutor);
sleepMainThread(POSTPONE_ANIMATION_MILLIS);
vertexAnim.syncPlay();
// 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 {
srcCell.getGeometry().setX(dstCell.getGeometry().getX() - overlapWidth);
srcCell.getGeometry().setY(dstCell.getGeometry().getY() - overlapHeight + (srcCellHeight * time));
} finally {
mxgraph.getModel().endUpdate();
}
}
destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
} else { // TODO: 仕様上のバグ、ループが発生
// 元の ObjectVertex
Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
Point2D dstParentCellAbsPt = getAbsolutePointforCell(dstCell.getParent());
// 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 (dstCell.getParent() != null
|| dstCell.getParent().getParent() != null
|| srcCell.getParent() != null
|| !dstCell.getParent().getParent().equals(getMxDefaultParent())
|| !srcCell.getParent().equals(dstCell.getParent())) {
srcCell.remove(dstCell.getParent());
dstCell.getParent().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);
srcCell.setParent(dstCell.getParent());
dstCell.getParent().insert(srcCell);
}
dstCell.getParent().getGeometry().setX(dstParentCellAbsPt.getX());
dstCell.getParent().getGeometry().setY(dstParentCellAbsPt.getY());
srcCell.getGeometry().setX(srcCellAbsPt.getX() - dstParentCellAbsPt.getX());
srcCell.getGeometry().setY(srcCellAbsPt.getY() - dstParentCellAbsPt.getY());
srcCell.setStyle("opacity=50;shape=ellipse");
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcCellWidth = srcCell.getGeometry().getWidth();
double srcCellHeight = srcCell.getGeometry().getHeight();
double overlapWidth = srcCellWidth - (srcCellWidth * Math.sqrt(2) * 0.1);
double overlapHeight = srcCellHeight - (srcCellHeight * Math.sqrt(2) * 0.1);
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(srcCell,
dstCell.getGeometry().getX() - overlapWidth + (srcCellWidth * time), dstCell.getGeometry().getY() - overlapHeight + (srcCellHeight * time),
threadPoolExecutor);
vertexAnim.syncPlay();
// 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 {
srcCell.getGeometry().setX(dstCell.getGeometry().getX() - overlapWidth + (srcCellWidth * time));
srcCell.getGeometry().setY(dstCell.getGeometry().getY() - overlapHeight + (srcCellHeight * time));
} 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 srcCell = (mxICell)sourceObjectVertex.getCell();
mxICell dstCell = (mxICell) destinationMethodExecutionVertex.getCell();
if (srcCell.equals(dstCell.getParent())) {
System.out.println("Nothing to moveActualArgumentObjectVertex().");
return;
}
// Remove sourceVertex from Locals and Arguments of MethodExecution's Vertex.
if (methodExecToVertexMap.containsKey(methodExecution) && methodExecToVertexMap.get(methodExecution).getLocals().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(methodExecution).getLocals().remove(sourceObjectVertex);
}
if (methodExecToVertexMap.containsKey(methodExecution) && methodExecToVertexMap.get(methodExecution).getArguments().contains(sourceObjectVertex)) {
methodExecToVertexMap.get(methodExecution).getArguments().remove(sourceObjectVertex);
}
int time = destinationMethodExecutionVertex.getLocals().size();
double srcCellCoordX = srcCell.getGeometry().getX();
double srcCellCoordY = srcCell.getGeometry().getY();
MagnetRONAnimation.waitAnimationEnd();
// 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(srcCell.getParent().getValue() != null) {
Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
srcCellCoordX = srcCellAbsPt.getX();
srcCellCoordY = srcCellAbsPt.getY();
srcCell.getParent().remove(srcCell);
}
if (srcCell.getParent() == null || !srcCell.getParent().equals(dstCell.getParent())) {
// TODO: Confirm why not need following comment out.
// if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
srcCell.setParent(dstCell.getParent());
dstCell.getParent().insert(srcCell);
}
Point2D srcParentCellAbsPt = getAbsolutePointforCell(srcCell.getParent());
srcCell.getGeometry().setX(srcCellCoordX - srcParentCellAbsPt.getX());
srcCell.getGeometry().setY(srcCellCoordY - srcParentCellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
double srcCellWidth = srcCell.getGeometry().getWidth();
double dstCellHeight = dstCell.getGeometry().getHeight();
Point2D srcCellDstPoint = new Point2D.Double(dstCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(3)) + (srcCellWidth * time),
dstCell.getGeometry().getY() + dstCellHeight);
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(srcCell, srcCellDstPoint.getX(), srcCellDstPoint.getY(), threadPoolExecutor);
vertexAnim.syncPlay();
// 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 {
srcCell.getGeometry().setX(srcCellDstPoint.getX());
srcCell.getGeometry().setY(srcCellDstPoint.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
}
/**
* Update size and position of all {@code ObjectVertex}.
*/
protected void updateObjectVertices() {
MagnetRONAnimation.waitAnimationEnd();
for (ObjectVertex ov: objectToVertexMap.values()) {
mxICell ovCell = (mxICell) ov.getCell();
if (ovCell == null) continue;
Dimension2D curDim = new Dimension((int) ovCell.getGeometry().getWidth(), (int) ovCell.getGeometry().getHeight());
int sizeScale = 0;
for (int i = 0; i < ovCell.getChildCount(); i++) {
if (!ovCell.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(ovCell.getGeometry().getX(), ovCell.getGeometry().getY());
if(!curDim.equals(dstDim)) {
// Test code (will be deleted)
System.out.println(TAG + ": Update size of ObjectVertex " + ovCell.getId() + ". " + curDim.getWidth() + "->" + dstDim.getWidth());
if (!ovCell.getParent().equals(getMxDefaultParent())
&& (ovCell.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(ovCell.getGeometry().getX() - overlapX, ovCell.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(ov) && locals.indexOf(ov) >= 1) {
overlapY = (dstDim.getHeight() - ovCell.getGeometry().getHeight()) / 2;
dstPt.setLocation(dstPt.getX(), ovCell.getGeometry().getY() + overlapY);
break;
}
List<ObjectVertex> arguments = methodExecVertex.getArguments();
if (arguments != null && arguments.contains(ov)) {
dstPt.setLocation(dstPt.getX(), ovCell.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 " + ovCell.getId() + ". Current point=" + ovCell.getGeometry().getPoint() + ", Destination Point=" + dstPt);
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(ovCell, dstPt.getX(), dstPt.getY(), threadPoolExecutor);
vertexAnim.play();
MagnetRONAnimation vertexResizeAnim = new VertexResizeAnimation(mxgraph, getGraphComponent());
vertexResizeAnim.setTotalCycleCount(10);
vertexResizeAnim.setDelay(100);
vertexResizeAnim.init(ovCell, dstDim.getWidth(), dstDim.getHeight(), threadPoolExecutor);
vertexResizeAnim.play();
for (int i = 0; i < ovCell.getChildCount(); i++) {
mxICell childCell = ovCell.getChildAt(i);
double childCellCurX = childCell.getGeometry().getX();
double childCellCurY = childCell.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 " + childCell.getId() + " of " + ovCell.getId() + ". Current point=" + childCell.getGeometry().getPoint() + ", Destination Point=" + childDstPt);
MagnetRONAnimation childVertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
childVertexAnim.setTotalCycleCount(10);
childVertexAnim.setDelay(100);
childVertexAnim.init(childCell, childDstPt.getX(), childDstPt.getY(), threadPoolExecutor);
childVertexAnim.play();
}
MagnetRONAnimation.waitAnimationEnd();
}
}
}
abstract protected void createMethodExecutionVertex(Alias alias);
/**
* Parent: Create {@code MethodExecutionVertex}.
*
* @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.equals("0")) {
objectId = methodExecution.getCallerMethodExecution().getThisObjId();
}
mxICell parentCell = (mxICell) objectToVertexMap.get(objectId).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 time = objectToVertexMap.get(objectId).getVertexMethodExecutions().size();
if (time >= 1) {
mxICell stdCell = (mxICell) objectToVertexMap.get(objectId).getVertexMethodExecutions().get(0).getCell();
stdX = stdCell.getGeometry().getX();
stdY = stdCell.getGeometry().getY();
time -= 1;
}
mxICell cell = null;
MethodExecutionVertex methodExecVertex = 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.
cell = (mxICell) mxgraph.insertDeltaVertex(parentCell, methodSignature, methodSignature, "fillColor=white");
mxgraph.orderCells(false, new Object[] {cell});
methodExecVertex =
new MethodExecutionVertex(methodSignature, cell, stdX, coordY * (time + 1) + stdY,
DEFAULT_METHOD_EXECUTION_VERTEX_SIZE.getWidth(), DEFAULT_METHOD_EXECUTION_VERTEX_SIZE.getHeight());
methodExecToVertexMap.put(methodExecution, methodExecVertex);
if(methodExecToVertexMap.size() > 1) {
// Caution: If synchronized block is split here, {@code cell} is visible instantly until cell#setVisible(false) is executed.
cell.setVisible(false);
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if(methodExecToVertexMap.size() > 1) {
createEdgesToMethodExecutions();
}
objectToVertexMap.get(objectId).addMethodExecution(methodExecVertex);
// setCellsStyle();
update();
}
/**
* Remove {@code MethodExecutionVertex} on AliasType is {@code AliasType.METHOD_INVOCATION}.
*
* @param alias
*/
private void removeMethodExecutionVertex(Alias alias) {
// source {@code ObjectVertex}
ObjectVertex srcObjVertex = objectToVertexMap.get(alias.getObjectId());
MethodExecution methodExec = alias.getMethodExecution();
if(alias.getAliasType().equals(AliasType.METHOD_INVOCATION) || alias.getAliasType().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 vo: arguments) {
if (vo != srcObjVertex) {
mxICell cell = (mxICell)vo.getCell();
if (!cell.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 cellAbsPt = getAbsolutePointforCell(cell);
cell.getParent().remove(cell);
cell.setParent(getMxDefaultParent());
cell.getGeometry().setX(cellAbsPt.getX());
cell.getGeometry().setY(cellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(cell, vo.getInitialX(), vo.getInitialY(), threadPoolExecutor);
vertexAnim.play();
methodExecToVertexMap.get(calledMethodExec).getArguments().remove(vo);
}
}
}
if (locals.size() != 0) {
for (ObjectVertex vo: locals) {
if (vo != srcObjVertex) {
mxICell cell = (mxICell)vo.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 cellAbsPt = getAbsolutePointforCell(cell);
if (!cell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
cell.getParent().remove(cell);
cell.setParent(getMxDefaultParent());
}
cell.getGeometry().setX(cellAbsPt.getX());
cell.getGeometry().setY(cellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(cell, vo.getInitialX(), vo.getInitialY(), threadPoolExecutor);
vertexAnim.play();
}
}
}
}
MagnetRONAnimation.waitAnimationEnd();
if (alias.getAliasType().equals(AliasType.CONSTRACTOR_INVOCATION)) {
sleepMainThread(500L);
}
removeCalledMethodExecutionVertex(srcObjVertex, methodExec, calledMethodExec);
} else {
removeMethodExecutionVertex(srcObjVertex, 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 dstMethodExecVertexCell = (mxCell)methodExecToVertexMap.get(methodExecution).getCell();
if (!dstMethodExecVertexCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstMethodExecVertexCell.getParent().remove(dstMethodExecVertexCell);
dstMethodExecVertexCell.setParent(getMxDefaultParent());
}
mxgraph.removeCells(new Object[] {dstMethodExecVertexCell});
objectToVertexMap.get(methodExecution.getThisObjId()).getVertexMethodExecutions().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 sourceObjectVertex from locals and arguments of called MethodExecutionVertex.
if (methodExecToVertexMap.containsKey(calledMethodExecution)) {
MethodExecutionVertex calledMethodExecVertex = methodExecToVertexMap.get(calledMethodExecution);
// TODO: Confirm bug.
List<ObjectVertex> arguments = new ArrayList<>(calledMethodExecVertex.getArguments());
if (arguments.size() != 0) {
for (ObjectVertex vo: arguments) {
if (vo != sourceObjectVertex) {
mxICell cell = (mxICell)vo.getCell();
Point2D cellAbsPt = getAbsolutePointforCell(cell);
// 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 (!cell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
cell.getParent().remove(cell);
cell.setParent(getMxDefaultParent());
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if (!cellAbsPt.equals(vo.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 {
cell.getGeometry().setX(cellAbsPt.getX());
cell.getGeometry().setY(cellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(cell, vo.getInitialX(), vo.getInitialY(), threadPoolExecutor);
vertexAnim.syncPlay();
}
methodExecToVertexMap.get(calledMethodExecution).getArguments().remove(vo);
}
}
}
List<ObjectVertex> locals = new ArrayList<>(calledMethodExecVertex.getLocals());
if (locals.size() != 0) {
for (ObjectVertex vo: locals) {
if (vo != sourceObjectVertex) {
mxICell cell = (mxICell)vo.getCell();
Point2D cellAbsPt = getAbsolutePointforCell(cell);
// 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 (!cell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
cell.getParent().remove(cell);
cell.setParent(getMxDefaultParent());
}
} finally {
mxgraph.getModel().endUpdate();
}
}
if (!cellAbsPt.equals(vo.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 {
cell.getGeometry().setX(cellAbsPt.getX());
cell.getGeometry().setY(cellAbsPt.getY());
} finally {
mxgraph.getModel().endUpdate();
}
}
MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
vertexAnim.setTotalCycleCount(10);
vertexAnim.setDelay(100);
vertexAnim.init(cell, vo.getInitialX(), vo.getInitialY(), threadPoolExecutor);
vertexAnim.syncPlay();
}
methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(vo);
}
}
}
if (methodExecution == null) {
return;
}
mxICell srcMethodExecVertexCell = (mxICell)methodExecToVertexMap.get(methodExecution).getCell();
mxICell dstMethodExecVertexCell = (mxICell)calledMethodExecVertex.getCell();
try {
Point2D srcMethodExecVertexCellAbsPt = null;
Point2D dstMethodExecVertexCellAbsPt = null;
final mxICell[] clonedstMethodExecVertexCell = 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(srcMethodExecVertexCell, dstMethodExecVertexCell));
srcMethodExecVertexCellAbsPt = getAbsolutePointforCell(srcMethodExecVertexCell);
dstMethodExecVertexCellAbsPt = getAbsolutePointforCell(dstMethodExecVertexCell);
clonedstMethodExecVertexCell[0] = (mxICell) mxgraph.addCell(dstMethodExecVertexCell.clone());
clonedstMethodExecVertexCell[0].getGeometry().setX(dstMethodExecVertexCellAbsPt.getX());
clonedstMethodExecVertexCell[0].getGeometry().setY(dstMethodExecVertexCellAbsPt.getY());
clonedstMethodExecVertexCell[0].setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
clonedstMethodExecVertexCell[0].setValue(null);
mxICell tmpEdge = (mxICell) mxgraph.insertEdge(getMxDefaultParent(), null, null, srcMethodExecVertexCell, clonedstMethodExecVertexCell[0]);
tmpEdge.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 edgeAnim = new TranslateAnimation(mxgraph, getGraphComponent());
edgeAnim.setTotalCycleCount(10);
edgeAnim.setDelay(100);
edgeAnim.init(clonedstMethodExecVertexCell[0], srcMethodExecVertexCellAbsPt.getX(), srcMethodExecVertexCellAbsPt.getY() + srcMethodExecVertexCell.getGeometry().getHeight(), threadPoolExecutor);
edgeAnim.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[]{clonedstMethodExecVertexCell[0]});
// TODO: Confirm execution order.
if (!dstMethodExecVertexCell.getParent().equals(getMxDefaultParent())) {
// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
dstMethodExecVertexCell.getParent().remove(dstMethodExecVertexCell);
dstMethodExecVertexCell.setParent(getMxDefaultParent());
}
mxgraph.removeCells(new Object[] {dstMethodExecVertexCell});
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
}
});
edgeAnim.play();
if (!calledMethodExecution.isStatic()) {
objectToVertexMap.get(calledMethodExecution.getThisObjId()).getVertexMethodExecutions().remove(methodExecToVertexMap.get(calledMethodExecution));
} else {
objectToVertexMap.get(calledMethodExecution.getCallerMethodExecution().getThisObjId()).getVertexMethodExecutions().remove(methodExecToVertexMap.get(calledMethodExecution));
}
methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(sourceObjectVertex);
methodExecToVertexMap.remove(calledMethodExecution);
edgeMap.remove(methodExecution.getSignature());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
sleepMainThread(POSTPONE_ANIMATION_MILLIS);
}
}
/**
* Create an edge between method execution 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 srcMethodExecVertexCell = (mxICell)methodExecToVertexMap.get(srcMethodExec).getCell();
mxICell dstMethodExecVertexCell = (mxICell)methodExecToVertexMap.get(dstMethodExec).getCell();
Point2D srcMethodExecVertexCellAbsPt = getAbsolutePointforCell(srcMethodExecVertexCell);
Point2D dstMethodExecVertexCellAbsPt = getAbsolutePointforCell(dstMethodExecVertexCell);
MagnetRONAnimation.waitAnimationEnd();
try {
final mxICell[] clonedstMethodExecVertexCell = 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 {
clonedstMethodExecVertexCell[0] = (mxICell) mxgraph.addCell(dstMethodExecVertexCell.clone());
clonedstMethodExecVertexCell[0].getGeometry().setX(srcMethodExecVertexCellAbsPt.getX());
clonedstMethodExecVertexCell[0].getGeometry().setY(srcMethodExecVertexCellAbsPt.getY() + dstMethodExecVertexCell.getGeometry().getHeight());
clonedstMethodExecVertexCell[0].setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
clonedstMethodExecVertexCell[0].setValue(null);
clonedstMethodExecVertexCell[0].setVisible(true);
mxICell tmpEdge = (mxICell) mxgraph.insertEdge(getMxDefaultParent(), null, null, srcMethodExecVertexCell, clonedstMethodExecVertexCell[0]);
tmpEdge.setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
dstMethodExecVertexCell.setVisible(true);
} finally {
mxgraph.getModel().endUpdate();
}
}
// Animate an edge to stretch.
MagnetRONAnimation edgeAnim = new TranslateAnimation(mxgraph, getGraphComponent());
edgeAnim.setTotalCycleCount(10);
edgeAnim.setDelay(100);
edgeAnim.init(clonedstMethodExecVertexCell[0], dstMethodExecVertexCellAbsPt.getX(), dstMethodExecVertexCellAbsPt.getY(), threadPoolExecutor);
edgeAnim.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 edge = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), methodSig, null, srcMethodExecVertexCell, dstMethodExecVertexCell);
if (!edge.getParent().equals(getMxDefaultParent())) {
// If parent of Edge cell isn't mxDefaltParent, reset parent.
edge.getParent().remove(edge);
edge.setParent(getMxDefaultParent());
}
mxgraph.orderCells(false, new Object[] {edge});
edge.setStyle("exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;");
edgeMap.put(methodSig, new Edge(methodSig, TypeName.Call, edge));
mxgraph.removeCells(new Object[]{clonedstMethodExecVertexCell[0]});
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
}
});
edgeAnim.play();
// 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 {
update();
} finally {
mxgraph.getModel().endUpdate();
}
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @param edge
* @param srcCellAbsPt
* @param dstCellAbsPt
*/
protected void setEdgePoint(mxICell edge, Point2D srcCellAbsPt, Point2D dstCellAbsPt) {
// mxgraph.orderCells(true, new Object[] {edge});
// if(srcCellAbsPt.getX() <= dstCellAbsPt.getX()) {
// // 右下から左上へエッジを引く
// edge.setStyle("exitX=0.5;exitY=0.5;exitPerimeter=1;entryX=0;entryY=0.5;entryPerimeter=1;");
// } else {
// // 左下から右上へエッジを引く
// edge.setStyle("exitX=0.5;exitY=0.5;exitPerimeter=1;entryX=1;entryY=0.5;entryPerimeter=1;");
// }
}
/**
* Styles all cells on the graph.
*/
protected void setCellsStyle() {
List<Object> objectVertex = new ArrayList<>();
List<Object> staticObjectVertex = new ArrayList<>();
List<Object> alignMidObjectVertex = new ArrayList<>();
List<Object> alignTopObjectVertex = new ArrayList<>();
List<Object> refEdge = new ArrayList<>();
List<Object> refCreateEdge = new ArrayList<>();
List<Object> methodExecEdge = new ArrayList<>();
List<Object> roundEdge = new ArrayList<>();
for (Entry<String, ObjectVertex> objectToVertexEntry: objectToVertexMap.entrySet()) {
String key = objectToVertexEntry.getKey();
ObjectVertex ov = objectToVertexEntry.getValue();
if (key.startsWith("0")) {
staticObjectVertex.add(ov.getCell());
} else {
objectVertex.add(ov.getCell());
}
if(ov.getVertexMethodExecutions().size() == 0) {
alignMidObjectVertex.add(ov.getCell());
} else {
alignTopObjectVertex.add(ov.getCell());
}
}
List<MethodExecutionVertex> methodExecVertexList = new ArrayList<>(methodExecToVertexMap.values());
Collections.reverse(methodExecVertexList);
for (int i = 0; i < methodExecVertexList.size(); i++) {
switch(i) {
case 0:
((mxICell)methodExecVertexList.get(i).getCell()).setStyle("fillColor=#ff7fbf");
break;
case 1:
((mxICell)methodExecVertexList.get(i).getCell()).setStyle("fillColor=#ff99cc");
break;
case 2:
((mxICell)methodExecVertexList.get(i).getCell()).setStyle("fillColor=#ffb2d8");
break;
case 3:
((mxICell)methodExecVertexList.get(i).getCell()).setStyle("fillColor=#ffcce5");
break;
case 4:
((mxICell)methodExecVertexList.get(i).getCell()).setStyle("fillColor=#ffe0ef");
break;
default:
break;
}
}
for (Edge edge: edgeMap.values()) {
roundEdge.add(edge.getCell());
switch(edge.getTypeName()) {
case Reference:
refEdge.add(edge.getCell());
break;
case Create:
refEdge.add(edge.getCell());
refCreateEdge.add(edge.getCell());
break;
case Call:
methodExecEdge.add(edge.getCell());
break;
default:
break;
}
}
// Styles ObjectVertex.
mxgraph.setCellStyles(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE, objectVertex.toArray(new Object[objectVertex.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE, objectVertex.toArray(new Object[objectVertex.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_UNDERLINE, true, objectVertex.toArray(new Object[objectVertex.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, alignMidObjectVertex.toArray(new Object[alignMidObjectVertex.size()]));
mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, alignTopObjectVertex.toArray(new Object[alignTopObjectVertex.size()]));
// Styles Edge.
mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM, refEdge.toArray(new Object[refEdge.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, refCreateEdge.toArray(new Object[refCreateEdge.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()]));
// Styles MethodExecutionVertex.
mxgraph.setCellStyles(mxConstants.STYLE_STROKECOLOR, "#008000", methodExecEdge.toArray(new Object[methodExecEdge.size()]));
mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, methodExecEdge.toArray(new Object[methodExecEdge.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();
mxgraphComponent.refresh();
}
private int countChildVertex(ObjectVertex objectVertex) {
int time = objectVertex.getVertexMethodExecutions().size();
if(time == 0) {
return 1;
}
for(MethodExecutionVertex mev: objectVertex.getVertexMethodExecutions()) {
for(ObjectVertex ov: mev.getLocals()) {
time += countChildVertex(ov);
}
for(ObjectVertex ov: mev.getArguments()) {
return countChildVertex(ov);
}
}
return time;
}
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();
}
private void setWindowSize(int width, int height) {
DEFAULT_WINDOW_SIZE.setSize(width, height);
}
/**
* 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.getVertexMethodExecutions()) {
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 sourceCell parents contain destinationCell.
*
* @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);
}
protected void reflectCoordinates(DeltaGraphAdapter mxgraph) {
// TODO Auto-generated method stub
}
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);
}
}
}
}