Newer
Older
MagnetRON / src / org / ntlab / deltaViewer / MagnetRONViewer.java
Aki Hongo on 20 Aug 2021 65 KB #5 Implemented so that method call and return value,
package org.ntlab.deltaViewer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionListener;
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.EdgeAnimation;
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.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;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.util.Duration;

/**

 * 
 * @author Nitta Lab.
 */
public abstract class MagnetRONViewer extends JPanel {

	private static final long serialVersionUID = -6828987937804142956L;

	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 = 1001;

	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 EdgeAnimation edgeAnimation;
	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());
		edgeAnimation = new EdgeAnimation(mxgraph, getGraphComponent());
		edgeAnimation.setTotalCycleCount(10);
		edgeAnimation.setDelay(100);
		threadPoolExecutor = new MagnetRONScheduledThreadPoolExecutor(2);
		
		getGraphComponent().setPreferredSize(DEFAULT_WINDOW_SIZE);
		setLayout(new BorderLayout());
		add(getGraphComponent(), BorderLayout.CENTER);
	}
	
	public mxGraphComponent getGraphComponent() {
		return mxgraphComponent;
	}
	
	public void clear() {
		mxgraph.getModel().beginUpdate();
		try {
			for (ObjectVertex ov: objectToVertexMap.values()) {
				mxICell ovCell = (mxICell)ov.getCell();
				if (ovCell != null) {
					if (!ovCell.getParent().equals(mxDefaultParent)) {
						// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
						ovCell.getParent().remove(ovCell);
						ovCell.setParent(mxDefaultParent);
					}
				}
			}
			mxgraph.removeCells(mxgraph.getChildVertices(mxDefaultParent));
		} 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" + i + ": " + alias.getAliasType().toString());
			System.out.println(alias.getObjectId() + ", " + 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.
				createObjectVertexOnConstractor(alias);
				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 (!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;
			// Test code (will be deleted)
			outputLog();
		}
	}

	/**
	 * 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);
		Point srcCellAbsPt = getAbsolutePointforCell(srcCell);
		MethodInvocation methodInv;
		String fieldName = null;
		if (!methodExec.isCollectionType() 
				&& alias.getOccurrencePoint().getStatement() != null) {
			methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
			fieldName = methodInv.getCallerSideMethodName();
		}
		mxgraph.getModel().beginUpdate();
		try {
			// Creates a white cell of {@code ObjectVertex}. 
			mxICell ovCell = 
					(mxICell) mxgraph.insertDeltaVertex(mxDefaultParent, 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(mxDefaultParent, fieldName, null, srcCell, ovCell);
//			setEdgePoint((mxCell)edge, srcCellAbsPt, objectVertex.getInitialPoint());
			edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
//			setCellsStyle();
			update();
			deltaAnimation.setVertexAnimation(ovCell, new mxPoint(objectVertex.getInitialX(), objectVertex.getInitialY()));
			deltaAnimation.startVertexAnimation();
			deltaAnimation.sleepThread(1001);
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}
	
	/**
	 * 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);
		Point srcCellAbsPt = getAbsolutePointforCell(srcCell);
		MethodInvocation methodInv;
		String fieldName = null;
		if (!methodExec.isCollectionType() && alias.getOccurrencePoint().getStatement() != null) {
			methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
			fieldName = methodInv.getCallerSideMethodName();
		}
		mxgraph.getModel().beginUpdate();
		try {
			// Creates a white cell of {@code ObjectVertex}. 
			mxICell ovCell = 
					(mxICell) mxgraph.insertDeltaVertex(mxDefaultParent, 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(mxDefaultParent, fieldName, null, srcCell, ovCell);
			setEdgePoint(edge, srcCellAbsPt, objectVertex.getInitialPoint());
			edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
//			setCellsStyle();
			update();
			deltaAnimation.setVertexAnimation(ovCell, new mxPoint(objectVertex.getInitialX(), objectVertex.getInitialY()));
			deltaAnimation.startVertexAnimation();
			deltaAnimation.sleepThread(1001);
		} finally {
			mxgraph.getModel().endUpdate();
		}		
	}

	/**
	 * 
	 * @param fieldUpdateStatement
	 * @param fieldName
	 */
	protected void createObjectRefrence(FieldUpdate fieldUpdateStatement, String fieldName) {
		String srcObjId = fieldUpdateStatement.getContainerObjId();
		String dstObjId = fieldUpdateStatement.getValueObjId();
		mxICell dstCell = (mxICell)objectToVertexMap.get(dstObjId).getCell();
		Point dstCellAbsPt = getAbsolutePointforCell(dstCell);

		mxgraph.getModel().beginUpdate();
		try {
			dstCell.getParent().remove(dstCell);
			dstCell.setParent(mxDefaultParent);
			dstCell.getGeometry().setX(dstCellAbsPt.getX());
			dstCell.getGeometry().setY(dstCellAbsPt.getY());
			mxICell edge = 
					(mxICell) mxgraph.insertDeltaEdge(mxDefaultParent, 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));
			deltaAnimation.setVertexAnimation(dstCell, new mxPoint(objectToVertexMap.get(dstObjId).getInitialX(), objectToVertexMap.get(dstObjId).getInitialY()));
			deltaAnimation.startVertexAnimation();
			deltaAnimation.sleepThread(DEFAULT_THREAD_SLEEP_MILLIS);
			dstCell.getGeometry().setX(objectToVertexMap.get(dstObjId).getInitialX());
			dstCell.getGeometry().setY(objectToVertexMap.get(dstObjId).getInitialY());
		} finally {
			mxgraph.getModel().endUpdate();
		}		
	}

	/**
	 * 
	 * @param sourceClassName
	 * @param sourceObjectId
	 * @param destinationObjectId
	 */
	protected void createObjectRefrence(String sourceClassName, String sourceObjectId, String destinationObjectId) {
		mxICell dstCell = (mxICell)objectToVertexMap.get(destinationObjectId).getCell();
		Point 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();
		try {
			dstCell.getParent().remove(dstCell);
			dstCell.setParent(mxDefaultParent);
			dstCell.getGeometry().setX(dstCellAbsPt.getX());
			dstCell.getGeometry().setY(dstCellAbsPt.getY());
			mxICell edge = 
					(mxICell) mxgraph.insertDeltaEdge(mxDefaultParent, destinationObjectId, null,
							objectToVertexMap.get(sourceObjectId).getCell(), objectToVertexMap.get(destinationObjectId).getCell());
			((mxCell)edge).setStyle("strokeColor=red;");
			edgeMap.put(destinationObjectId, new Edge(null, TypeName.Reference, edge));
			deltaAnimation.setVertexAnimation(dstCell, new mxPoint(objectToVertexMap.get(destinationObjectId).getInitialX(), objectToVertexMap.get(destinationObjectId).getInitialY()));
			deltaAnimation.startVertexAnimation();
			deltaAnimation.sleepThread(1001);
			dstCell.getGeometry().setX(objectToVertexMap.get(destinationObjectId).getInitialX());
			dstCell.getGeometry().setY(objectToVertexMap.get(destinationObjectId).getInitialY());
		} finally {
			mxgraph.getModel().endUpdate();
		}
		// Test code (will be deleted)
		System.out.println("rTHIS " + sourceClassName + ", " + destinationObjectId);
	}

	/**
	 * 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());
		}
		// destination {@code ObjectVertex}
		MethodExecutionVertex dstMethodExecVertex = methodExecToVertexMap.get(alias.getMethodExecution());
		// Test code (will be deleted)
		System.out.println("moveObjectVertex: " + dstMethodExecVertex);
		System.out.println("moveObjectVertex: " + alias.getMethodExecution().isStatic());
		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)) {
				deltaAnimation.sleepThread(1001);
			}
			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 sourceCell = (mxICell)sourceObjectVertex.getCell();
		mxICell destinationCell = (mxICell) destinationMethodExecutionVertex.getCell();

		if (sourceCell == destinationCell.getParent()) {
			// Test code (will be deleted)
			System.out.println("Nothing to moveLocalObjectVertex().");
			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);
			// Test code (will be deleted)
			System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeLocal: " + sourceObjectVertex.getLabel());
		}

		if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getArguments().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(callerMethodExecution).getArguments().remove(sourceObjectVertex);
			// Test code (will be deleted)
			System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeArgument: " + sourceObjectVertex.getLabel());
		}

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		try {
			int time = destinationMethodExecutionVertex.getLocals().size();
			double srcCellCoordX = sourceCell.getGeometry().getX();
			double srcCellCoordY = sourceCell.getGeometry().getY();

			if(sourceCell.getParent().getValue() != null) {
				Point absolutePointSourceCell = getAbsolutePointforCell(sourceCell);
				srcCellCoordX = absolutePointSourceCell.getX();
				srcCellCoordY = absolutePointSourceCell.getY();
				sourceCell.getParent().remove(sourceCell);
			}

			mxgraph.orderCells(true, new Object[] {sourceCell});
			sourceCell.setParent(destinationCell.getParent());
			destinationCell.getParent().insert(sourceCell);
			// Test code (will be deleted)
			System.out.println("moveLocalObjectVertex: " + sourceCell.getId() + " (" + sourceCell.hashCode() + ")" + ", " + sourceCell.getParent().getId() + " (" + sourceCell.getParent().hashCode() + ")");
			System.out.println("                       " + destinationCell.getId() + " (" + destinationCell.hashCode() + ")" + ", " + destinationCell.getParent().getId() + " (" + destinationCell.getParent().hashCode() + ")");

			Point dstCellAbsPt = getAbsolutePointforCell(sourceCell.getParent());
			sourceCell.getGeometry().setX(srcCellCoordX - dstCellAbsPt.getX());
			sourceCell.getGeometry().setY(srcCellCoordY - dstCellAbsPt.getY());

			double srcCellWidth = sourceCell.getGeometry().getWidth();
			double dstCellHeight = destinationCell.getGeometry().getHeight();

			deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(destinationCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(2.5)) + (srcCellWidth * time), destinationCell.getGeometry().getY() + dstCellHeight));
			deltaAnimation.startVertexAnimation();
			deltaAnimation.sleepThread(1001);
			
			sourceCell.setParent(destinationCell.getParent());
			destinationCell.getParent().insert(sourceCell);
			sourceCell.getGeometry().setX(destinationCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(2.5)) + (srcCellWidth * time));
			sourceCell.getGeometry().setY(destinationCell.getGeometry().getY() + dstCellHeight);
			destinationMethodExecutionVertex.getLocals().add(sourceObjectVertex);
			// Test code (will be deleted)
			System.out.println("moveLocalObjectVertex: " + destinationMethodExecutionVertex.getLabel() + " :Local: " + sourceObjectVertex.getLabel());
		} 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);
			// Test code (will be deleted)
			System.out.println(methodExecToVertexMap.get(callerMethodExec).getLabel() + " :removeLocal: " + sourceObjectVertex.getLabel());
		}

		if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getArguments().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(callerMethodExec).getArguments().remove(sourceObjectVertex);
			// Test code (will be deleted)
			System.out.println(methodExecToVertexMap.get(callerMethodExec).getLabel() + " :removeArgument: " + sourceObjectVertex.getLabel());
		}

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		try {
			int time = destinationMethodExecutionVertex.getArguments().size();
			double srcCoordX = srcCell.getGeometry().getX();
			double srcCoordY = srcCell.getGeometry().getY();

			if(srcCell.getParent().getValue() != null) {
				Point srcCellAbsPt = getAbsolutePointforCell(srcCell);
				srcCoordX = srcCellAbsPt.getX();
				srcCoordY = srcCellAbsPt.getY();
				srcCell.getParent().remove(srcCell);
			}

			if (!isParent(dstCell, srcCell)) {
				mxgraph.orderCells(true, new Object[] {srcCell});
				srcCell.setParent(dstCell.getParent());
				dstCell.getParent().insert(srcCell);
				Point srcParentCellAbsPt = getAbsolutePointforCell(srcCell.getParent());
				srcCell.getGeometry().setX(srcCoordX - srcParentCellAbsPt.getX());
				srcCell.getGeometry().setY(srcCoordY - srcParentCellAbsPt.getY());

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

				deltaAnimation.setVertexAnimation(srcCell, new mxPoint(dstCell.getGeometry().getX() - overlapWidth, dstCell.getGeometry().getY()  - overlapHeight + (srcCellHeight * time)));
				deltaAnimation.startVertexAnimation();
				deltaAnimation.sleepThread(1001);

				srcCell.getGeometry().setX(dstCell.getGeometry().getX() - overlapWidth);
				srcCell.getGeometry().setY(dstCell.getGeometry().getY() - overlapHeight + (srcCellHeight * time));
				destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
				// Test code (will be deleted)
				System.out.println("moveArgumentObejctVertex" + destinationMethodExecutionVertex.getLabel() + " :Argument: " + sourceObjectVertex.getLabel());				
			} else { // TODO: 仕様上のバグ、ループが発生
				// Test code (will be deleted)
				outputLog();

				// 元の{@code ObjectVertex}
				Point srcCellAbsPt = getAbsolutePointforCell(srcCell);
				Point dstParentCellAbsPt = getAbsolutePointforCell(dstCell.getParent());

				srcCell.remove(dstCell.getParent());
				dstCell.getParent().setParent(mxDefaultParent);
				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");

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

				deltaAnimation.setVertexAnimation(srcCell, new mxPoint(dstCell.getGeometry().getX() - overlapWidth + (srcCellWidth * time), dstCell.getGeometry().getY()  - overlapHeight + (srcCellHeight * time)));
				deltaAnimation.startVertexAnimation();
				deltaAnimation.sleepThread(DEFAULT_THREAD_SLEEP_MILLIS);
				// Test code (will be deleted)
				outputLog();
				srcCell.getGeometry().setX(dstCell.getGeometry().getX() - overlapWidth + (srcCellWidth * time));
				srcCell.getGeometry().setY(dstCell.getGeometry().getY()  - overlapHeight + (srcCellHeight * time));

				destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);			
			}
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}

	/**
	 * 
	 * @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 == dstCell.getParent()) {
			System.out.println("Nothing to moveActualArgumentObjectVertex().");
			return;
		}

		//  Remove sourceVertex from Locals and Arguments of MethodExecution's Vertex. 
		// Test code (will be deleted)
		System.out.println(methodExecution.getSignature());
		System.out.println(sourceObjectVertex.getLabel());
		if (methodExecToVertexMap.containsKey(methodExecution) && methodExecToVertexMap.get(methodExecution).getLocals().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(methodExecution).getLocals().remove(sourceObjectVertex);
			// Test code (will be deleted)
			System.out.println(methodExecToVertexMap.get(methodExecution).getLabel() + " :removeLocal: " + sourceObjectVertex.getLabel());
		}

		if (methodExecToVertexMap.containsKey(methodExecution) && methodExecToVertexMap.get(methodExecution).getArguments().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(methodExecution).getArguments().remove(sourceObjectVertex);
			// Test code (will be deleted)
			System.out.println(methodExecToVertexMap.get(methodExecution).getLabel() + " :removeArgument: " + sourceObjectVertex.getLabel());
		}

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		try {
			int time = destinationMethodExecutionVertex.getLocals().size();
			double srcCellCoordX = srcCell.getGeometry().getX();
			double srcCellCoordY = srcCell.getGeometry().getY();

			// Test code (will be deleted)
			System.out.println(time + ", " + destinationMethodExecutionVertex.getLocals().size());

			if(srcCell.getParent().getValue() != null) {
				Point srcCellAbsPt = getAbsolutePointforCell(srcCell);
				srcCellCoordX = srcCellAbsPt.getX();
				srcCellCoordY = srcCellAbsPt.getY();
				srcCell.getParent().remove(srcCell);
			}

			srcCell.setParent(dstCell.getParent());
			dstCell.getParent().insert(srcCell);
			Point srcParentCellAbsPt = getAbsolutePointforCell(srcCell.getParent());
			srcCell.getGeometry().setX(srcCellCoordX - srcParentCellAbsPt.getX());
			srcCell.getGeometry().setY(srcCellCoordY - srcParentCellAbsPt.getY());

			double srcCellWidth = srcCell.getGeometry().getWidth();
			double dstCellHeight = dstCell.getGeometry().getHeight();

			deltaAnimation.setVertexAnimation(srcCell, 
					new mxPoint(dstCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(3)) + (srcCellWidth * time), dstCell.getGeometry().getY() + dstCellHeight));
			deltaAnimation.startVertexAnimation();
			deltaAnimation.sleepThread(DEFAULT_THREAD_SLEEP_MILLIS);
			srcCell.getGeometry().setX(dstCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(3)) + (srcCellWidth * time));
			srcCell.getGeometry().setY(dstCell.getGeometry().getY() + dstCellHeight);

			destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
			// Test code (will be deleted)
			System.out.println("moveActualArgumentObjectVertex: " + destinationMethodExecutionVertex.getLabel() + " :Argument: " + sourceObjectVertex.getLabel());
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}

	/** 
	 * Update size and position of all {@code ObjectVertex}. 
	 */
	protected void updateObjectVertices() {
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology    	
		mxgraph.getModel().beginUpdate();
		try {
			for (ObjectVertex ov: objectToVertexMap.values()) {
				mxCell ovCell = (mxCell) ov.getCell();
				if (ovCell != null) {
					int time = 0;
					for (int i = 0; i < ovCell.getChildCount(); i++) {
						if (!(ovCell.getChildAt(i).getId()).contains("clone")) {
							time++;
						}
					}
					if (time == 0) { 
						time = 1;
					}
					if(ovCell.getGeometry().getWidth() != DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * time) {
						// Test code (will be deleted)
						System.out.println("updateVertexObjectSize: " + ovCell.getGeometry().getWidth() + "->" + DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * time+ ", " + ovCell.getId());
						Dimension dstSize = new Dimension();
						dstSize.setSize(DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * time, DEFAULT_OBJECT_VERTEX_SIZE.getHeight() * time);
						if (ovCell.getParent() != mxDefaultParent && (ovCell.getChildCount() != 0 || ovCell.getGeometry().getWidth() > DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * time)) {
							double overlapX = (dstSize.getWidth() - ovCell.getGeometry().getWidth()) / 2 / Math.sqrt(2);
							double overlapY = (dstSize.getHeight() - ovCell.getGeometry().getHeight()) / 2 / Math.sqrt(2);
							// Test code (will be deleted)
							System.out.println("updateVertexObjectPosition: " + ovCell.getGeometry().getX() + " - " + overlapX);
							mxPoint dstPt = new mxPoint(ovCell.getGeometry().getX() - overlapX, ovCell.getGeometry().getY() + overlapY);
							int idxFromParent = ovCell.getParent().getIndex(ovCell);
							if (idxFromParent >= 2) {
								overlapY = (dstSize.getHeight() - ovCell.getGeometry().getHeight()) / 2;
								dstPt.setY(ovCell.getGeometry().getY() + overlapY);
							}
							for (MethodExecutionVertex methodExecVertex: methodExecToVertexMap.values()) {
								List<ObjectVertex> arguments = methodExecVertex.getArguments();
								if (arguments != null && arguments.contains(ov)) {
									dstPt.setY(ovCell.getGeometry().getY() - overlapY);
									break;
								}
							}
							deltaAnimation.setVertexAnimation(ovCell, dstPt);
							deltaAnimation.startVertexAnimation();
						}
						deltaAnimation.setResizeVertexAnimation(ovCell, dstSize);
						deltaAnimation.startResizeVertexAnimation();
						deltaAnimation.sleepThread(DEFAULT_THREAD_SLEEP_MILLIS);
					}
				}
			}
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}

	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) {
		mxICell parentCell = (mxICell) objectToVertexMap.get(objectId).getCell();

//		if (cell == null) return;
		
		if (methodSignature == null) {
			methodSignature = methodExecution.getSignature();
		}

		if (methodSignature.matches(".+\\(.*\\)")) {
			// Test code (will be deleted)
			System.out.println(methodSignature);
			methodSignature = formatMethodSignature(methodSignature, methodExecution.getThisClassName());
		}

		if (methodExecution.isStatic() && !objectId.equals("0")) {
			objectId = methodExecution.getCallerMethodExecution().getThisObjId();
		}

		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();
		// Test code (will be deleted)
		System.out.println("time" + time);
		if (time >= 1) {
			mxICell stdCell = (mxICell) objectToVertexMap.get(objectId).getVertexMethodExecutions().get(0).getCell();
			stdX = stdCell.getGeometry().getX();
			stdY = stdCell.getGeometry().getY();
			time -= 1;
		}

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		try {
			// Creates a white cell of {@code ObjectVertex}. 
			mxICell cell = (mxICell) mxgraph.insertDeltaVertex(parentCell, methodSignature, methodSignature, "fillColor=white");
			mxgraph.orderCells(false, new Object[] {cell});
			// Test code (will be deleted)
			System.out.println("makeVertexMethodExecution: " + cell.getId() + " in " + objectId + " (" + stdX + ", " + coordY * (time + 1) + stdY + ")");

			MethodExecutionVertex 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) {
				cell.setVisible(false);
				createEdgeToMethodExecution();
			}
			objectToVertexMap.get(objectId).addMethodExecution(methodExecVertex);
		} finally {
			mxgraph.getModel().endUpdate();
		}
//		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();
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			try {
				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) {
							// Test code (will be deleted)
							System.out.println("argumentRemove");
							mxICell cell = (mxICell)vo.getCell();
							if (!cell.getParent().equals(mxDefaultParent)) {
								Point cellAbsPt = getAbsolutePointforCell(cell);
								cell.getParent().remove(cell);
								cell.setParent(mxDefaultParent);
								cell.getGeometry().setX(cellAbsPt.getX());
								cell.getGeometry().setY(cellAbsPt.getY());
								deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
								deltaAnimation.startVertexAnimation();
							}
							methodExecToVertexMap.get(calledMethodExec).getArguments().remove(vo);
						}
					}
				}
				if (locals.size() != 0) {
					for (ObjectVertex vo: locals) {
						if (vo != srcObjVertex) {
							// Test code (will be deleted)
							System.out.println("localRemove");
							mxICell cell = (mxICell)vo.getCell();
							Point cellAbsPt = getAbsolutePointforCell(cell);
							cell.getParent().remove(cell);
							cell.setParent(mxDefaultParent);
							cell.getGeometry().setX(cellAbsPt.getX());
							cell.getGeometry().setY(cellAbsPt.getY());
							deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
							deltaAnimation.startVertexAnimation();
							methodExecToVertexMap.get(calledMethodExec).getLocals().remove(vo);
						}
					}
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}

			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();
		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();
				dstMethodExecVertexCell.getParent().remove(dstMethodExecVertexCell);
				dstMethodExecVertexCell.setParent(mxDefaultParent);
				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) {	
		// Test code (will be deleted)
		outputLog();

		//  Remove ObjectVertex other than sourceObjectVertex from locals and arguments of called MethodExecutionVertex.  
		if (methodExecToVertexMap.containsKey(calledMethodExecution)) {
			MethodExecutionVertex calledMethodExecVertex = methodExecToVertexMap.get(calledMethodExecution);

			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			try {
				// 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();
							Point cellAbsPt = getAbsolutePointforCell(cell);
							// Test code (will be deleted)
							System.out.println(cell);
							System.out.println(vo.getInitialX() + ", " + vo.getInitialY());
							System.out.println(cell.getGeometry().getX() + ", " +  cell.getGeometry().getY());
							System.out.println(cellAbsPt);
							if (cell.getParent() != mxDefaultParent) {
								cell.getParent().remove(cell);
								cell.setParent(mxDefaultParent);
							}
							if (!cellAbsPt.equals(vo.getInitialPoint())) {
								cell.getGeometry().setX(cellAbsPt.getX());
								cell.getGeometry().setY(cellAbsPt.getY());
								deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
								deltaAnimation.startVertexAnimation();
								deltaAnimation.sleepThread(DEFAULT_THREAD_SLEEP_MILLIS);
							}
							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();
							Point cellAbsPt = getAbsolutePointforCell(cell);
							// Test code (will be deleted)
							System.out.println(cell);
							System.out.println(vo.getInitialX() + ", " + vo.getInitialY());
							System.out.println(cell.getGeometry().getX() + ", " +  cell.getGeometry().getY());
							System.out.println(cellAbsPt);
							if (cell.getParent() != mxDefaultParent) {
								cell.getParent().remove(cell);
								cell.setParent(mxDefaultParent);
							}
							if (!cellAbsPt.equals(vo.getInitialPoint())) {
								cell.getGeometry().setX(cellAbsPt.getX());
								cell.getGeometry().setY(cellAbsPt.getY());
								deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
								deltaAnimation.startVertexAnimation();
								deltaAnimation.sleepThread(DEFAULT_THREAD_SLEEP_MILLIS);
							}
							methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(vo);
						}
					}
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}
						
			if (methodExecution == null) {
				return;
			}
			
			mxICell srcMethodExecVertexCell = (mxICell)methodExecToVertexMap.get(methodExecution).getCell();
			mxICell dstMethodExecVertexCell = (mxICell)calledMethodExecVertex.getCell();
				
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			try {
				mxgraph.removeCells(mxgraph.getEdgesBetween(srcMethodExecVertexCell, dstMethodExecVertexCell));
				Point srcMethodExecVertexCellAbsPt = getAbsolutePointforCell(srcMethodExecVertexCell);
				Point dstMethodExecVertexCellAbsPt = getAbsolutePointforCell(dstMethodExecVertexCell);
				mxICell clonedstMethodExecVertexCell = (mxICell) mxgraph.addCell(dstMethodExecVertexCell.clone());
				clonedstMethodExecVertexCell.getGeometry().setX(dstMethodExecVertexCellAbsPt.getX());
				clonedstMethodExecVertexCell.getGeometry().setY(dstMethodExecVertexCellAbsPt.getY());
				clonedstMethodExecVertexCell.setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
				clonedstMethodExecVertexCell.setValue(null);
				mxICell tmpEdge = (mxICell) mxgraph.insertEdge(mxDefaultParent, null, null, srcMethodExecVertexCell, clonedstMethodExecVertexCell);
				tmpEdge.setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");

				// Animate an edge to shrink.
				edgeAnimation.init(clonedstMethodExecVertexCell, new Point2D.Double(srcMethodExecVertexCellAbsPt.getX(), srcMethodExecVertexCellAbsPt.getY() + srcMethodExecVertexCell.getGeometry().getHeight()), threadPoolExecutor);
				// Test code (will be deleted)
				System.out.println("absPointSourceVertexCell: " + srcMethodExecVertexCellAbsPt);
				edgeAnimation.setOnFinished(new ActionListener() {
					@Override
					public void actionPerformed(java.awt.event.ActionEvent e) {
						// Test code (will be deleted)
						System.out.println("Shrink edge animation action performed. ");
						mxgraph.removeCells(new Object[]{clonedstMethodExecVertexCell});

						// TODO: Confirm execution order.
						dstMethodExecVertexCell.getParent().remove(dstMethodExecVertexCell);
						dstMethodExecVertexCell.setParent(mxDefaultParent);
						mxgraph.removeCells(new Object[] {dstMethodExecVertexCell});
					}
				});
				edgeAnimation.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());		
				
				edgeAnimation.sleepThread(201);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		// Test code (will be deleted)
		outputLog();
	}
	
	/**
	 * Create an edge between method execution While animating the edge to stretch.
	 */
	private void createEdgeToMethodExecution() {
		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();
				Point srcMethodExecVertexCellAbsPt = getAbsolutePointforCell(srcMethodExecVertexCell);
				Point dstMethodExecVertexCellAbsPt = getAbsolutePointforCell(dstMethodExecVertexCell);

				// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
				mxgraph.getModel().beginUpdate();
				try {
					mxICell clonedstMethodExecVertexCell = (mxICell) mxgraph.addCell(dstMethodExecVertexCell.clone());
					clonedstMethodExecVertexCell.getGeometry().setX(srcMethodExecVertexCellAbsPt.getX());
					clonedstMethodExecVertexCell.getGeometry().setY(srcMethodExecVertexCellAbsPt.getY() + dstMethodExecVertexCell.getGeometry().getHeight());
					clonedstMethodExecVertexCell.setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
					clonedstMethodExecVertexCell.setValue(null);
					clonedstMethodExecVertexCell.setVisible(true);
					mxICell tmpEdge = (mxICell) mxgraph.insertEdge(mxDefaultParent, null, null, srcMethodExecVertexCell, clonedstMethodExecVertexCell);
					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);
					
					// Animate an edge to stretch.
					edgeAnimation.init(clonedstMethodExecVertexCell, dstMethodExecVertexCellAbsPt, threadPoolExecutor);
					// Test code (will be deleted)
					System.out.println("absPointSourceVertexCell: " + srcMethodExecVertexCellAbsPt);
					edgeAnimation.setOnFinished(new ActionListener() {
						@Override
						public void actionPerformed(java.awt.event.ActionEvent e) {
							// Test code (will be deleted)
							System.out.println("Stretch edge animation action performed. ");
							mxICell edge = (mxICell) mxgraph.insertDeltaEdge(mxDefaultParent, methodSig, null, srcMethodExecVertexCell, dstMethodExecVertexCell);
							edge.getParent().remove(((mxCell)edge));
							edge.setParent(mxDefaultParent);
							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});
							update();
						}
					});
					edgeAnimation.play();
					update();
					edgeAnimation.sleepThread(201);
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}
		}
	}

	/**
	 * 
	 * @param edge
	 * @param srcCellAbsPt
	 * @param dstCellAbsPt
	 */
	protected void setEdgePoint(mxICell edge, Point srcCellAbsPt, Point 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.matches("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()]));
	}

	private void setWindowSize(int width, int height) {
		DEFAULT_WINDOW_SIZE.setSize(width, height);
	}

	protected Point getAbsolutePointforCell(mxICell cell) {
		Point p1 = cell.getGeometry().getPoint();
		if(cell.getParent().getValue() == null || cell == cell.getParent()) {
			return p1;
		}
		// Test code (will be deleted)
		System.out.println(cell.getId() + ", " + cell.getParent().getId());
		Point p2 = getAbsolutePointforCell(cell.getParent());
		return new Point((int) (p1.getX() + p2.getX()), (int) (p1.getY() + p2.getY()));
	}

	/**
	 *  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);
			}
		}
		// Test code (will be deleted)
		System.out.println(objectVertex.getLabel() + ": " + time);
		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();
	}

	/**
	 * Test code (will be deleted)
	 */
	protected void outputLog() {
		for (Object obj: mxgraph.getChildCells(mxDefaultParent)) {
			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 srcPtCell = sourceCell.getParent();
		if (srcPtCell == null || srcPtCell.getValue() == null) {
			return false;
		}
		if (srcPtCell == destinationCell) {
			return true;
		}
		// Test code (will be deleted)
		System.out.println(sourceCell.getId() + ", " + srcPtCell.getId());
		return isParent(srcPtCell, destinationCell);
	}

	protected void reflectCoordinates(DeltaGraphAdapter mxgraph) {
		// TODO Auto-generated method stub
	}
	
	public static Map.Entry<Reference, String> getRelatedInformation(TracePoint relatedPoint) {
		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(rpStatement instanceof FieldUpdate) {
			// Format fieldName.
			FieldUpdate rpFieldUpdateStatement = (FieldUpdate) rpStatement;
			rpSrcObjId = rpFieldUpdateStatement.getContainerObjId();
			rpDstObjId = rpFieldUpdateStatement.getValueObjId();
			String rpFieldNames[] = formatFieldName(rpFieldUpdateStatement.getFieldName());
			rpSrcClassName = rpFieldNames[0];
			rpDstClassName = rpFieldUpdateStatement.getValueClassName();
			rpFieldName = rpFieldNames[rpFieldNames.length-1];
		} 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);		
	}
	
	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);
			}
		}
	}
}