Newer
Older
MagnetRON / src / org / ntlab / deltaViewer / MagnetRONViewer.java
Aki Hongo on 19 Sep 2021 84 KB #67 Implemented the feature to fast forward animation
package org.ntlab.deltaViewer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.awt.geom.Dimension2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ThreadPoolExecutor;

import javax.swing.JPanel;

import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedWeightedPseudograph;
import org.ntlab.animations.MagnetRONAnimation;
import org.ntlab.animations.TranslateAnimation;
import org.ntlab.animations.VertexResizeAnimation;
import org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.IAliasCollector;
import org.ntlab.deltaExtractor.Alias.AliasType;
import org.ntlab.deltaViewer.Edge.TypeName;
import org.ntlab.trace.ArrayUpdate;
import org.ntlab.trace.FieldUpdate;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.MethodInvocation;
import org.ntlab.trace.ObjectReference;
import org.ntlab.trace.Reference;
import org.ntlab.trace.Statement;
import org.ntlab.trace.TracePoint;

import com.mxgraph.canvas.mxGraphics2DCanvas;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxICell;
import com.mxgraph.shape.mxConnectorShape;
import com.mxgraph.shape.mxIShape;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.view.mxInteractiveCanvas;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;

/**

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

	private static final long serialVersionUID = -6828987937804142956L;

	// Test code (will be deleted)
	private static final String TAG = MagnetRONViewer.class.getSimpleName();
	
	protected static final Dimension DEFAULT_COMPONENT_SIZE = new Dimension(1300, 700);

	protected static final Dimension DEFAULT_OBJECT_VERTEX_SIZE = new Dimension(70, 70);
	protected static final Dimension DEFAULT_METHOD_EXECUTION_VERTEX_SIZE = new Dimension(55, 20);

	protected IAliasCollector aliasCollector;

	protected Map<String, ObjectVertex> objectToVertexMap = new HashMap<>();
	protected Map<MethodExecution, MethodExecutionVertex> methodExecToVertexMap = new LinkedHashMap<>();
	protected Map<String, Edge> edgeMap = new HashMap<>();

	protected mxGraphComponent mxgraphComponent;
	protected DeltaGraphAdapter mxgraph;
	protected mxICell mxDefaultParent;

	protected int curFrame = 0;
	
	protected double animationSpeed = DEFAULT_ANIMATION_SPEED;
	protected static final double DEFAULT_ANIMATION_SPEED = 1.0;

	protected ThreadPoolExecutor threadPoolExecutor;
	protected long animationDelayMillis = DEFAULT_ANIMATION_DELAY_MILLIS;
	protected static final long DEFAULT_ANIMATION_DELAY_MILLIS = 250L;
	protected int magnetRONAnimationTotalCycleCount = DEFAULT_MAGNETRON_ANIMATION_TOTAL_CYCLE_COUNT;
	protected static final int DEFAULT_MAGNETRON_ANIMATION_TOTAL_CYCLE_COUNT = 10;
	protected long magnetRONAnimationDelayMillis = DEFAULT_MAGNETRON_ANIMATION_DELAY_MILLIS;
	protected static final long DEFAULT_MAGNETRON_ANIMATION_DELAY_MILLIS = 100L;

	private boolean fAutoTracking = false;
	
	public MagnetRONViewer() {
		mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph(DefaultEdge.class));
		mxDefaultParent = (mxCell)mxgraph.getDefaultParent();
		mxgraphComponent = new mxGraphComponent(mxgraph) {
			public mxInteractiveCanvas createCanvas() {
				return new CurvedCanvas(this);
			}
		};
		mxgraphComponent.setPreferredSize(DEFAULT_COMPONENT_SIZE);		
		this.setLayout(new BorderLayout());
		this.add(mxgraphComponent, BorderLayout.CENTER);
		
		threadPoolExecutor = new MagnetRONScheduledThreadPoolExecutor(2);
	}
	
	public mxGraphComponent getGraphComponent() {
		return mxgraphComponent;
	}

	protected mxICell getMxDefaultParent() {
		return mxDefaultParent;
	}
	
	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().startsWith("0:")) {
					createMethodExecutionVertex(alias);
					update();
				}
				break;
			case RECEIVER:
				// Make {@code MethodExecutionVertex} of called method execution.
				MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution();
				if (calledMethodExec.isConstructor() 
						&& (!objectToVertexMap.containsKey(alias.getObjectId()) || objectToVertexMap.get(alias.getObjectId()).getCell() == null)) {
					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 a {@code mxIcell} of {@code ObjectVertex} while animating {@code TranslateAnimation} 
	 * when {@link Alias#getAliasType()} is CONSTRACTOR_INVOCATION.
	 * 
	 * @param alias
	 */
	protected void createObjectVertexOnConstractor(Alias alias) {
		ObjectVertex objVx = objectToVertexMap.get(alias.getObjectId());
		mxICell objVxCell = null;
		
		// Position of srcCell is start point for ovCell to TranslateAnimation.
		MethodExecution methodExec = alias.getMethodExecution();
		String srcObjId = methodExec.getThisObjId();
		if (srcObjId.matches("0")) {
			srcObjId += ":" + alias.getMethodExecution().getThisClassName();
		}
		mxICell srcObjVxCell = (mxICell) objectToVertexMap.get(srcObjId).getCell();
		double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
		double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
		double overlapWid = srcObjVxCellWid * Math.sqrt(2) * 0.1;
		double overlapHt = srcObjVxCellHt  - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
		Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
				
		MethodInvocation methodInv;
		String fieldName = null;
		if (!methodExec.isCollectionType() 
				&& alias.getOccurrencePoint().getStatement() != null) {
			methodInv = (MethodInvocation) alias.getOccurrencePoint().getStatement();
			fieldName = methodInv.getCallerSideMethodName();
		}
		
		MagnetRONAnimation.waitAnimationEnd();
		scrollCellAndPointToVisible(srcObjVxCell, objVx.getInitialPoint(), 2);

		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				// Creates a white mxICell of ObjectVertex. 
				objVxCell = 
						(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), alias.getObjectId(), objVx.getLabel(), 
								srcObjVxCellAbsPt.getX() + overlapWid, srcObjVxCellAbsPt.getY() + overlapHt, 
								DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(), 
								"fillColor=white");
				objVx.setCell(objVxCell);
				mxICell edgeCell = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcObjVxCell, objVxCell);
				edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edgeCell));
				update();
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
		objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
		objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
		objVxCellAnim.syncPlay();
	}
	
	/**
	 * Create a {@code mxIcell} of {@code ObjectVertex} while animating {@code TranslateAnimation} when {@link Alias#getAliasType()} is CONSTRACTOR_INVOCATION preceded by FORMAL_PARAMETER.
	 * 
	 * @param alias
	 */
	protected void createObjectVertexOnConstractorByFormalParameter(Alias alias) {
		ObjectVertex objVx = objectToVertexMap.get(alias.getObjectId()); // Create mxICell of this ObjectVertex.
		MethodExecution methodExec = alias.getMethodExecution();
		mxICell objVxCell = null;
		
		// Position of srcCell is start point for ovCell to TranslateAnimation.
		String srcObjId = methodExec.getArguments().get(0).getId();
		mxICell srcObjVxCell = (mxICell) objectToVertexMap.get(srcObjId).getCell();
		double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
		double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
		double overlapWidth = srcObjVxCellWid * Math.sqrt(2) * 0.1;
		double overlapHeight = srcObjVxCellHt  - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
		Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
		
		scrollCellAndPointToVisible(srcObjVxCell, objVx.getInitialPoint(), 2);

		MethodInvocation methodInv;
		String fieldName = null;
		if (!methodExec.isCollectionType() && alias.getOccurrencePoint().getStatement() != null) {
			methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
			fieldName = methodInv.getCallerSideMethodName();
		}
		
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				// Creates a white mxIcell of ObjectVertex. 
				objVxCell = 
						(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), objVx.getLabel(), objVx.getLabel(), 
								srcObjVxCellAbsPt.getX() + overlapWidth, srcObjVxCellAbsPt.getY() + overlapHeight, 
								DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(), 
								"fillColor=white");
				objVx.setCell(objVxCell);
				mxICell edgeCell = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcObjVxCell, objVxCell);
				edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edgeCell));
				update();
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
		objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
		objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
		objVxCellAnim.syncPlay();
	}

	/**
	 * Create edge of {@code ObjectReference}.
	 * 
	 * @param fieldUpdateStatement
	 * @param fieldName
	 */
	protected void createObjectRefrence(FieldUpdate fieldUpdateStatement, String fieldName) {
		// Create edge between source ObjectVertex mxICell and destination ObjectVertex mxICell.
		String srcObjId = fieldUpdateStatement.getContainerObjId();
		mxICell srcObjVxCell = (mxICell) objectToVertexMap.get(srcObjId).getCell();
		
		String dstObjId = fieldUpdateStatement.getValueObjId();
		ObjectVertex dstObjVx = objectToVertexMap.get(dstObjId);
		mxICell dstObjVxCell = (mxICell) dstObjVx.getCell();
		Point2D dstObjVxCellAbsPt = getAbsolutePointforCell(dstObjVxCell);

		scrollCellAndPointToVisible(srcObjVxCell, dstObjVx.getInitialPoint(), 2);
		
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if (!dstObjVxCell.getParent().equals(getMxDefaultParent())) {
					// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
					dstObjVxCell.getParent().remove(dstObjVxCell);
					dstObjVxCell.setParent(getMxDefaultParent());
				}
				dstObjVxCell.getGeometry().setX(dstObjVxCellAbsPt.getX());
				dstObjVxCell.getGeometry().setY(dstObjVxCellAbsPt.getY());
				mxICell edgeCell = 
						(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldUpdateStatement.getFieldName(), fieldName, 
								srcObjVxCell, dstObjVxCell);
				edgeCell.setStyle("strokeColor=red;");
//				mxgraph.orderCells(true, new Object[] {edge});
				edgeMap.put(fieldUpdateStatement.getFieldName(), new Edge(fieldName, TypeName.Reference, edgeCell));
			} finally {
				mxgraph.getModel().endUpdate();
			}			
		}
		MagnetRONAnimation dstObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		dstObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
		dstObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
		dstObjVxCellAnim.init(dstObjVxCell, dstObjVx.getInitialX(), dstObjVx.getInitialY(), threadPoolExecutor);
		dstObjVxCellAnim.syncPlay();
		
		// If the animation didn't work to the end.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				dstObjVxCell.getGeometry().setX(dstObjVx.getInitialX());
				dstObjVxCell.getGeometry().setY(dstObjVx.getInitialY());
			} finally {
				mxgraph.getModel().endUpdate();
			}			
		}
	}

	/**
	 *  Create edge of {@code ObjectReference}.
	 *  
	 * @param sourceClassName
	 * @param sourceObjectId
	 * @param destinationObjectId
	 */
	protected void createObjectRefrence(String sourceClassName, String sourceObjectId, String destinationObjectId) {
		// Create edge between source ObjectVertex mxICell and destination ObjectVertex mxICell.
		mxICell srcObjVxCell = (mxICell)objectToVertexMap.get(sourceObjectId).getCell();

		ObjectVertex dstObjVx = objectToVertexMap.get(destinationObjectId);
		mxICell dstObjVxCell = (mxICell) dstObjVx.getCell();
		Point2D dstObjVxCellAbsPt = getAbsolutePointforCell(dstObjVxCell);

		scrollCellAndPointToVisible(srcObjVxCell, dstObjVx.getInitialPoint(), 2);

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if (!dstObjVxCell.getParent().equals(getMxDefaultParent())) {
					// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
					dstObjVxCell.getParent().remove(dstObjVxCell);
					dstObjVxCell.setParent(getMxDefaultParent());
				}
				dstObjVxCell.getGeometry().setX(dstObjVxCellAbsPt.getX());
				dstObjVxCell.getGeometry().setY(dstObjVxCellAbsPt.getY());
				mxICell edgeCell = 
						(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), destinationObjectId, null,
								srcObjVxCell, dstObjVxCell);
				edgeCell.setStyle("strokeColor=red;");
				edgeMap.put(destinationObjectId, new Edge(null, TypeName.Reference, edgeCell));
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		MagnetRONAnimation dstObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		dstObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
		dstObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
		dstObjVxCellAnim.init(dstObjVxCell, dstObjVx.getInitialX(), dstObjVx.getInitialY(), threadPoolExecutor);
		dstObjVxCellAnim.syncPlay();
		
		// If the animation didn't work to the end.
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				dstObjVxCell.getGeometry().setX(dstObjVx.getInitialX());
				dstObjVxCell.getGeometry().setY(dstObjVx.getInitialY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
	}

	/**
	 * Move to position of destination {@code ObjectVertex} from source {@code ObjectVertex}.
	 * 
	 * @param alias
	 */
	protected void moveObjectVertex(Alias alias) {
		// source ObjectVertex
		String objId = alias.getObjectId();
		ObjectVertex srcObjVx = objectToVertexMap.get(objId);
		MethodExecution methodExec = alias.getMethodExecution();
		if (!methodExecToVertexMap.containsKey(methodExec) && methodExec.isStatic()) {
			createMethodExecutionVertex(objId, methodExec.getSignature(), methodExec);
			sleepMainThread(getAnimationDelayMillis());
		}
		// destination ObjectVertex
		MethodExecutionVertex dstMethodExecVx = methodExecToVertexMap.get(methodExec);
		moveObjectVertex(alias, srcObjVx, dstMethodExecVx);
		updateObjectVertices();
	}

	/**
	 * Parent: Move to position of destination {@code ObjectVertex} from source {@code ObjectVertex}.
	 * 
	 * @param alias
	 * @param sourceVertexObject: source {@code ObjectVertex}
	 * @param destinationVertexMethodExec: destination {@code MethodExecutionVertex}
	 */
	private void moveObjectVertex(Alias alias, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
		MethodExecution methodExec = alias.getMethodExecution();
		AliasType aliasType = alias.getAliasType();
		if (aliasType.equals(AliasType.RETURN_VALUE) || aliasType.equals(AliasType.METHOD_INVOCATION)) {
			if (sourceObjectVertex.getCell() == null && methodExec.isCollectionType()) {
				if (methodExec.getArguments().isEmpty()) {
					createObjectVertexOnConstractor(alias);
				} else {
					createObjectVertexOnConstractorByFormalParameter(alias);
				}
			}
			if (aliasType.equals(AliasType.RETURN_VALUE)) {
				MagnetRONAnimation.waitAnimationEnd();
			}
			moveLocalObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
		} else if (aliasType.equals(AliasType.FORMAL_PARAMETER)) {
			moveArgumentObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);
		} else if (aliasType.equals(AliasType.ACTUAL_ARGUMENT)) {
			moveActualArgumentObjectVertex(methodExec, sourceObjectVertex, destinationMethodExecutionVertex);			
		}
	}

	/**
	 * Move to local position of destination {@code MethodExecutionVertex} from caller {@code MethodExecution} of source {@code ObjectVertex}. 
	 * 
	 * @param callerMethodExecution: caller {@code MethodExecution}
	 * @param sourceObjectVertex
	 * @param destinationMethodExecutionVertex
	 */
	private void moveLocalObjectVertex(MethodExecution callerMethodExecution, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
		mxICell srcObjVxCell = (mxICell)sourceObjectVertex.getCell();
		mxICell dstMethodExecVxCell = (mxICell) destinationMethodExecutionVertex.getCell();

		if (srcObjVxCell.equals(dstMethodExecVxCell.getParent())) {
			return;
		}

		scrollCellsToVisible(dstMethodExecVxCell.getParent(), srcObjVxCell);

		// Remove source ObjectVertex from Locals and Arguments of caller MethodExecutionVertex.  
		if (methodExecToVertexMap.containsKey(callerMethodExecution)) {
			MethodExecutionVertex callerMethodExecVx = methodExecToVertexMap.get(callerMethodExecution);
			if (callerMethodExecVx.getLocals().contains(sourceObjectVertex)) {
				callerMethodExecVx.getLocals().remove(sourceObjectVertex);
			}
			if (callerMethodExecVx.getArguments().contains(sourceObjectVertex)) {
				callerMethodExecVx.getArguments().remove(sourceObjectVertex);
			}
		}

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				double srcObjVxCellX = srcObjVxCell.getGeometry().getX();
				double srcObjVxCellY = srcObjVxCell.getGeometry().getY();

				// TODO: Confirm why not .equals(getMxDefaultParent()).
				if(srcObjVxCell.getParent().getValue() != null) {
					Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
					srcObjVxCellX = srcObjVxCellAbsPt.getX();
					srcObjVxCellY = srcObjVxCellAbsPt.getY();
					srcObjVxCell.getParent().remove(srcObjVxCell);
				}
				mxgraph.orderCells(true, new Object[] {srcObjVxCell});
				if (srcObjVxCell.getParent() == null || !srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
					// TODO: Confirm why not need following comment out.
//					if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
					srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
					dstMethodExecVxCell.getParent().insert(srcObjVxCell);
				}

				Point2D dstObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell.getParent());
				srcObjVxCell.getGeometry().setX(srcObjVxCellX - dstObjVxCellAbsPt.getX());
				srcObjVxCell.getGeometry().setY(srcObjVxCellY - dstObjVxCellAbsPt.getY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}

		int dstMethodExecVxLocalsSize = destinationMethodExecutionVertex.getLocals().size();
		double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
		double dstMethodExecVxCellHt = dstMethodExecVxCell.getGeometry().getHeight();
		double srcObjVxCellDstX = dstMethodExecVxCell.getGeometry().getX() - (srcObjVxCellWid / Math.sqrt(2.5)) + (srcObjVxCellWid * dstMethodExecVxLocalsSize);
		double srcObjVxCellDstY = dstMethodExecVxCell.getGeometry().getY() + dstMethodExecVxCellHt;

		MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
		srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
		srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstX, srcObjVxCellDstY, threadPoolExecutor);
		srcObjVxCellAnim.syncPlay();
		
		// If the animation didn't work to the end.
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if (!srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
					srcObjVxCell.getParent().remove(srcObjVxCell);
					srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
					dstMethodExecVxCell.getParent().insert(srcObjVxCell);
				}
				srcObjVxCell.getGeometry().setX(srcObjVxCellDstX);
				srcObjVxCell.getGeometry().setY(srcObjVxCellDstY);
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		destinationMethodExecutionVertex.getLocals().add(sourceObjectVertex);
	}

	/**
	 * Move to position of destination {@code MethodExecutionVertex}'s argument from {@code MethodExecution} of source {@code VertexObject}.
	 * 
	 * @param methodExecution: {@code MethodExecution}
	 * @param sourceVertexObject: moving {@code VertexObject}
	 * @param destinationMethodExecutionVertex
	 */
	protected void moveArgumentObjectVertex(MethodExecution methodExecution, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
		mxICell srcObjVxCell = (mxICell)sourceObjectVertex.getCell();
		mxICell dstMethodExecVxCell = (mxICell) destinationMethodExecutionVertex.getCell();

		// Remove source VertexObject from Locals and Arguments of MethodExecutionVertex.  
		MethodExecution callerMethodExec = methodExecution.getCallerMethodExecution();
		if (methodExecToVertexMap.containsKey(callerMethodExec)) {
			MethodExecutionVertex callerMethodExecVx = methodExecToVertexMap.get(callerMethodExec);
			mxICell callerMethodExecVxCell = (mxICell) callerMethodExecVx.getCell();
			scrollCellsToVisible(callerMethodExecVxCell.getParent(), dstMethodExecVxCell.getParent(), 2);
			if (callerMethodExecVx.getLocals().contains(sourceObjectVertex)) {
				callerMethodExecVx.getLocals().remove(sourceObjectVertex);
			}
			if (callerMethodExecVx.getArguments().contains(sourceObjectVertex)) {
				callerMethodExecVx.getArguments().remove(sourceObjectVertex);
			}
		} else {
			scrollCellsToVisible(srcObjVxCell, dstMethodExecVxCell.getParent(), 2);			
		}

		int dstMethodExecVxArgumentsSize = destinationMethodExecutionVertex.getArguments().size();
		double srcObjVxCellX = srcObjVxCell.getGeometry().getX();
		double srcObjVxCellY = srcObjVxCell.getGeometry().getY();

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if(srcObjVxCell.getParent().getValue() != null) {
					Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
					srcObjVxCellX = srcObjVxCellAbsPt.getX();
					srcObjVxCellY = srcObjVxCellAbsPt.getY();
					srcObjVxCell.getParent().remove(srcObjVxCell);
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}

		if (!isParent(dstMethodExecVxCell, srcObjVxCell)) {
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			synchronized (mxgraph.getModel()) {
				try {
					mxgraph.orderCells(true, new Object[] {srcObjVxCell});
					if (srcObjVxCell.getParent() == null || !srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
						// TODO: Confirm why not need following comment out.
//						if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
						srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
						dstMethodExecVxCell.getParent().insert(srcObjVxCell);
					}
					Point2D srcObjVxCellParentAbsPt = getAbsolutePointforCell(srcObjVxCell.getParent());
					srcObjVxCell.getGeometry().setX(srcObjVxCellX - srcObjVxCellParentAbsPt.getX());
					srcObjVxCell.getGeometry().setY(srcObjVxCellY - srcObjVxCellParentAbsPt.getY());
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}

			double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
			double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
			double overlapWid = srcObjVxCellWid - (srcObjVxCellWid * Math.sqrt(2) * 0.1);
			double overlapHt = srcObjVxCellHt - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
			Point2D srcObjVxCellDstPt = 
					new Point2D.Double(dstMethodExecVxCell.getGeometry().getX() - overlapWid, 
							dstMethodExecVxCell.getGeometry().getY()  - overlapHt + (srcObjVxCellHt * dstMethodExecVxArgumentsSize));
						
			MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
			srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
			srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
			srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstPt.getX(), srcObjVxCellDstPt.getY(), threadPoolExecutor);
			sleepMainThread(getAnimationDelayMillis());
			srcObjVxCellAnim.syncPlay();

			// If the animation didn't work to the end.
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			synchronized (mxgraph.getModel()) {
				try {
					srcObjVxCell.getGeometry().setX(srcObjVxCellDstPt.getX());
					srcObjVxCell.getGeometry().setY(srcObjVxCellDstPt.getY());
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}
			destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
		} else { // TODO: 仕様上のバグ、ループが発生
			// 元の ObjectVertex
			Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
			mxICell dstMethodExecVxParentCell = dstMethodExecVxCell.getParent();
			Point2D dstMethodExecVxParentCellAbsPt = getAbsolutePointforCell(dstMethodExecVxParentCell);

			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			synchronized (mxgraph.getModel()) {
				try {
					if ((dstMethodExecVxParentCell != null && dstMethodExecVxParentCell.getParent() != null)
							|| srcObjVxCell.getParent() != null
							|| !dstMethodExecVxParentCell.getParent().equals(getMxDefaultParent())
							|| !srcObjVxCell.getParent().equals(dstMethodExecVxParentCell)) {					
						srcObjVxCell.remove(dstMethodExecVxParentCell);
						dstMethodExecVxParentCell.setParent(getMxDefaultParent());
						// TODO: Confirm why not need following comment out.
//						dstCell.getParent().remove(dstCell);
						// TODO: Confirm why not need following comment out.
//						srcCell.getParent().remove(srcCell);
						srcObjVxCell.setParent(dstMethodExecVxParentCell);
						dstMethodExecVxParentCell.insert(srcObjVxCell);
					}

					dstMethodExecVxParentCell.getGeometry().setX(dstMethodExecVxParentCellAbsPt.getX());
					dstMethodExecVxParentCell.getGeometry().setY(dstMethodExecVxParentCellAbsPt.getY());
					srcObjVxCell.getGeometry().setX(srcObjVxCellAbsPt.getX() - dstMethodExecVxParentCellAbsPt.getX());
					srcObjVxCell.getGeometry().setY(srcObjVxCellAbsPt.getY() - dstMethodExecVxParentCellAbsPt.getY());
					srcObjVxCell.setStyle("opacity=50;shape=ellipse");
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}

			double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
			double srcObjVxCellHt = srcObjVxCell.getGeometry().getHeight();
			double overlapWid = srcObjVxCellWid - (srcObjVxCellWid * Math.sqrt(2) * 0.1);
			double overlapHt = srcObjVxCellHt - (srcObjVxCellHt * Math.sqrt(2) * 0.1);
			double srcObjVxCellDstX = dstMethodExecVxCell.getGeometry().getX() - overlapWid + (srcObjVxCellWid * dstMethodExecVxArgumentsSize);
			double srcObjVxCellDstY = dstMethodExecVxCell.getGeometry().getY()  - overlapHt + (srcObjVxCellHt * dstMethodExecVxArgumentsSize);
					
			MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
			srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
			srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
			srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstX, srcObjVxCellDstY, threadPoolExecutor);
			srcObjVxCellAnim.syncPlay();

			// If the animation didn't work to the end.
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			synchronized (mxgraph.getModel()) {
				try {
					srcObjVxCell.getGeometry().setX(srcObjVxCellDstX);
					srcObjVxCell.getGeometry().setY(srcObjVxCellDstY);
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}
			destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);			
		}
	}

	/**
	 * 
	 * @param methodExecution: {@code MethodExecution}
	 * @param sourceVertexObject: moving {@code VertexObject}
	 * @param destinationVertexMethodExec
	 */

	/**
	 * Move to position of destination {@code MethodExecutionVertex}'s actual argument from {@code MethodExecution} of source {@code VertexObject}.
	 * 
	 * @param methodExecution: {@code MethodExecution}
	 * @param sourceObjectVertex
	 * @param destinationMethodExecutionVertex
	 */
	private void moveActualArgumentObjectVertex(MethodExecution methodExecution, ObjectVertex sourceObjectVertex, MethodExecutionVertex destinationMethodExecutionVertex) {
		mxICell srcObjVxCell = (mxICell)sourceObjectVertex.getCell();
		mxICell dstMethodExecVxCell = (mxICell) destinationMethodExecutionVertex.getCell();

		if (srcObjVxCell.equals(dstMethodExecVxCell.getParent())) {
			return;
		}

		//  Remove source ObjectVertex from Locals and Arguments of MethodExecutionVertex. 
		if (methodExecToVertexMap.containsKey(methodExecution)) {
			if (methodExecToVertexMap.get(methodExecution).getLocals().contains(sourceObjectVertex)) {
				methodExecToVertexMap.get(methodExecution).getLocals().remove(sourceObjectVertex);
			}
			if (methodExecToVertexMap.get(methodExecution).getArguments().contains(sourceObjectVertex)) {
				methodExecToVertexMap.get(methodExecution).getArguments().remove(sourceObjectVertex);
			}
		}
		
		int dstMethodExecVxLocalsSize = destinationMethodExecutionVertex.getLocals().size();
		double srcObjVxCellX = srcObjVxCell.getGeometry().getX();
		double srcObjVxCellY = srcObjVxCell.getGeometry().getY();

		MagnetRONAnimation.waitAnimationEnd();
		scrollCellsToVisible(srcObjVxCell, dstMethodExecVxCell.getParent(), 2);			
		
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if(srcObjVxCell.getParent().getValue() != null) {
					Point2D srcObjVxCellAbsPt = getAbsolutePointforCell(srcObjVxCell);
					srcObjVxCellX = srcObjVxCellAbsPt.getX();
					srcObjVxCellY = srcObjVxCellAbsPt.getY();
					srcObjVxCell.getParent().remove(srcObjVxCell);
				}

				if (srcObjVxCell.getParent() == null 
						|| !srcObjVxCell.getParent().equals(dstMethodExecVxCell.getParent())) {
					// TODO: Confirm why not need following comment out.
//					if (srcCell.getParent() != null) srcCell.getParent().remove(srcCell);
					srcObjVxCell.setParent(dstMethodExecVxCell.getParent());
					dstMethodExecVxCell.getParent().insert(srcObjVxCell);
				}
				Point2D srcObjVxCellParentAbsPt = getAbsolutePointforCell(srcObjVxCell.getParent());
				srcObjVxCell.getGeometry().setX(srcObjVxCellX - srcObjVxCellParentAbsPt.getX());
				srcObjVxCell.getGeometry().setY(srcObjVxCellY - srcObjVxCellParentAbsPt.getY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}

		double srcObjVxCellWid = srcObjVxCell.getGeometry().getWidth();
		double dstMethodExecVxCellHt = dstMethodExecVxCell.getGeometry().getHeight();
		Point2D srcObjVxCellDstPt = new Point2D.Double(dstMethodExecVxCell.getGeometry().getX() - (srcObjVxCellWid / Math.sqrt(3)) + (srcObjVxCellWid * dstMethodExecVxLocalsSize), 
				dstMethodExecVxCell.getGeometry().getY() + dstMethodExecVxCellHt);
				
		MagnetRONAnimation srcObjVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		srcObjVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
		srcObjVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
		srcObjVxCellAnim.init(srcObjVxCell, srcObjVxCellDstPt.getX(), srcObjVxCellDstPt.getY(), threadPoolExecutor);
		srcObjVxCellAnim.syncPlay();

		// If the animation didn't work to the end.
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				srcObjVxCell.getGeometry().setX(srcObjVxCellDstPt.getX());
				srcObjVxCell.getGeometry().setY(srcObjVxCellDstPt.getY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}

		destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
	}

	/** 
	 * Update size and position of all {@code ObjectVertex}. 
	 */
	protected void updateObjectVertices() {
		MagnetRONAnimation.waitAnimationEnd();
		for (ObjectVertex objVx: objectToVertexMap.values()) {
			mxICell objVxCell = (mxICell) objVx.getCell();
			if (objVxCell == null) continue;
			Dimension2D curDim = new Dimension((int) objVxCell.getGeometry().getWidth(), (int) objVxCell.getGeometry().getHeight());
			int sizeScale = 0;
			for (int i = 0; i < objVxCell.getChildCount(); i++) {
				if (!objVxCell.getChildAt(i).getId().contains("clone")) sizeScale++;
			}
			if (sizeScale == 0) sizeScale = 1;
			Dimension2D dstDim = 
					new Dimension((int) DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * sizeScale, 
							(int) DEFAULT_OBJECT_VERTEX_SIZE.getHeight() * sizeScale);
			Point2D dstPt = new Point2D.Double(objVxCell.getGeometry().getX(), objVxCell.getGeometry().getY());

			if(!curDim.equals(dstDim)) {
				// Test code (will be deleted)
				System.out.println(TAG + ": Update size of ObjectVertex " + objVxCell.getId() + ". " + curDim.getWidth() + "->" + dstDim.getWidth());
				if (!objVxCell.getParent().equals(getMxDefaultParent()) 
						&& (objVxCell.getChildCount() != 0 || (curDim.getWidth() > dstDim.getWidth() && curDim.getHeight() > dstDim.getHeight()))) {
					double overlapX = (dstDim.getWidth() - curDim.getWidth()) / 2 / Math.sqrt(2);
					double overlapY = (dstDim.getHeight() - curDim.getHeight()) / 2 / Math.sqrt(2);
					dstPt.setLocation(objVxCell.getGeometry().getX() - overlapX, objVxCell.getGeometry().getY() + overlapY);

					// If two and over ObjectVertex are arranged side by side as an argument or local, shift the Y coordinate of ObjectVertex slightly.
					for (MethodExecutionVertex methodExecVertex: methodExecToVertexMap.values()) {
						List<ObjectVertex> locals = methodExecVertex.getLocals();
						if (locals != null && locals.contains(objVx) && locals.indexOf(objVx) >= 1) {
							overlapY = (dstDim.getHeight() - objVxCell.getGeometry().getHeight()) / 2;
							dstPt.setLocation(dstPt.getX(), objVxCell.getGeometry().getY() + overlapY);
							break;
						}
						List<ObjectVertex> arguments = methodExecVertex.getArguments();
						if (arguments != null && arguments.contains(objVx)) {
							dstPt.setLocation(dstPt.getX(), objVxCell.getGeometry().getY() - overlapY);
							break;
						}
					}
				}
				dstPt.setLocation(dstPt.getX() - (dstDim.getWidth() - curDim.getWidth()) / 2, dstPt.getY() - (dstDim.getHeight() - curDim.getHeight()) / 2);
				// Test code (will be deleted)
				System.out.println(TAG + ": Translate " + objVxCell.getId() + ". Current point=" + objVxCell.getGeometry().getPoint() + ", Destination Point=" + dstPt);
				MagnetRONAnimation objVxCellTransAnim = new TranslateAnimation(mxgraph, getGraphComponent());
				objVxCellTransAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
				objVxCellTransAnim.setDelay(getMagnetRONAnimationDelayMillis());
				objVxCellTransAnim.init(objVxCell, dstPt.getX(), dstPt.getY(), threadPoolExecutor);
				objVxCellTransAnim.play();
				MagnetRONAnimation objVxCellResizeAnim = new VertexResizeAnimation(mxgraph, getGraphComponent());
				objVxCellResizeAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
				objVxCellResizeAnim.setDelay(getMagnetRONAnimationDelayMillis());
				objVxCellResizeAnim.init(objVxCell, dstDim.getWidth(), dstDim.getHeight(), threadPoolExecutor);
				objVxCellResizeAnim.play();
				for (int i = 0; i < objVxCell.getChildCount(); i++) {
					mxICell objVxCellChild = objVxCell.getChildAt(i);
					double childCellCurX = objVxCellChild.getGeometry().getX();
					double childCellCurY = objVxCellChild.getGeometry().getY();
					Point2D childDstPt = 
							new Point2D.Double(childCellCurX + (dstDim.getWidth() - curDim.getWidth()) / 2, 
									childCellCurY + (dstDim.getHeight() - curDim.getHeight()) / 2);
					// Test code (will be deleted)
					System.out.println(TAG + ": Translate " + objVxCellChild.getId() + " of " + objVxCell.getId() + ". Current point=" + objVxCellChild.getGeometry().getPoint() + ", Destination Point=" + childDstPt);
					MagnetRONAnimation childCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
					childCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
					childCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
					childCellAnim.init(objVxCellChild, childDstPt.getX(), childDstPt.getY(), threadPoolExecutor);
					childCellAnim.play();
				}
				MagnetRONAnimation.waitAnimationEnd();
			}
		}
	}

	abstract protected void createMethodExecutionVertex(Alias alias);

	/**
	 * Create {@code MethodExecutionVertex} and Call {@link MagnetRONViewer#createEdgesToMethodExecutions()}.
	 * 
	 * @param objectId
	 * @param methodSignature: called or this method signature
	 * @param methodExec: called or this {@code MethodExecution}
	 */
	protected void createMethodExecutionVertex(String objectId, String methodSignature, MethodExecution methodExecution) {
		if (methodSignature == null) {
			methodSignature = methodExecution.getSignature();
		}

		if (methodSignature.matches(".+\\(.*\\)")) {
			methodSignature = formatMethodSignature(methodSignature, methodExecution.getThisClassName());
		}

		// TODO: Confirm why is the following code needed?		
		if (methodExecution.isStatic() && !objectId.startsWith("0")) {
			objectId = methodExecution.getCallerMethodExecution().getThisObjId();
			if (objectId.matches("0")) {
				objectId += ":" + methodExecution.getCallerMethodExecution().getThisClassName();
			}
		}

		ObjectVertex parentObjVx = objectToVertexMap.get(objectId);
		mxICell parentObjVxCell = (mxICell) parentObjVx.getCell();
		double coordX = DEFAULT_OBJECT_VERTEX_SIZE.getWidth() * 0.1;
		double coordY = DEFAULT_OBJECT_VERTEX_SIZE.getHeight() * 0.5;
		double stdX = coordX;
		double stdY = 0;
		int methodExecVxSize = parentObjVx.getMethodExecutionVertices().size();
		if (methodExecVxSize >= 1) {
			mxICell stdCell = (mxICell) parentObjVx.getMethodExecutionVertices().get(0).getCell();
			stdX = stdCell.getGeometry().getX();
			stdY = stdCell.getGeometry().getY();
			methodExecVxSize -= 1;
		}

		mxICell methodExecVxCell = null;
		MethodExecutionVertex methodExecVx = null;
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				// Creates a white cell of MethodExecutionVertex. 
				methodExecVxCell = (mxICell) mxgraph.insertDeltaVertex(parentObjVxCell, methodSignature, methodSignature, "fillColor=white");
				mxgraph.orderCells(false, new Object[] {methodExecVxCell});
				methodExecVx = 
						new MethodExecutionVertex(methodSignature, methodExecVxCell, stdX, coordY * (methodExecVxSize + 1) + stdY, 
								DEFAULT_METHOD_EXECUTION_VERTEX_SIZE.getWidth(), DEFAULT_METHOD_EXECUTION_VERTEX_SIZE.getHeight());
				methodExecToVertexMap.put(methodExecution, methodExecVx);
				if(methodExecToVertexMap.size() > 1) {
					// Caution: If synchronized block is split here, {@code cell} is visible instantly until cell#setVisible(false) is executed.
					methodExecVxCell.setVisible(false);
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		
		if(methodExecToVertexMap.size() > 1) {
			createEdgesToMethodExecutions();
		}
		parentObjVx.addMethodExecutionVertex(methodExecVx);
		update();
	}

	/**
	 * Remove {@code MethodExecutionVertex} on {@code {@link Alias#getAliasType()} is {@code AliasType.METHOD_INVOCATION}.
	 * 
	 * @param alias
	 */
	private void removeMethodExecutionVertex(Alias alias) {
		// source ObjectVertex
		ObjectVertex srcObjVx = objectToVertexMap.get(alias.getObjectId());
		MethodExecution methodExec = alias.getMethodExecution();

		AliasType aliasType = alias.getAliasType();
		if(aliasType.equals(AliasType.METHOD_INVOCATION) || aliasType.equals(AliasType.CONSTRACTOR_INVOCATION)) {
			MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution();
			List<ObjectVertex> arguments = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getArguments());
			List<ObjectVertex> locals = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getLocals());
			if (arguments.size() != 0) {
				for (ObjectVertex objVx: arguments) {
					// TODO: Implement equals().
					if (objVx != srcObjVx) {
						mxICell objVxcell = (mxICell)objVx.getCell();
						if (!objVxcell.getParent().equals(getMxDefaultParent())) {
							// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxcell);
									objVxcell.getParent().remove(objVxcell);
									objVxcell.setParent(getMxDefaultParent());
									objVxcell.getGeometry().setX(objVxCellAbsPt.getX());
									objVxcell.getGeometry().setY(objVxCellAbsPt.getY());
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
							MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
							objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
							objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
							objVxCellAnim.init(objVxcell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
							objVxCellAnim.play();
							methodExecToVertexMap.get(calledMethodExec).getArguments().remove(objVx);
						}
					}
				}
				if (locals.size() != 0) {
					for (ObjectVertex objVx: locals) {
						if (objVx != srcObjVx) {
							mxICell objVxCell = (mxICell) objVx.getCell();
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxCell);
									if (!objVxCell.getParent().equals(getMxDefaultParent())) {
										// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
										objVxCell.getParent().remove(objVxCell);
										objVxCell.setParent(getMxDefaultParent());
									}
									objVxCell.getGeometry().setX(objVxCellAbsPt.getX());
									objVxCell.getGeometry().setY(objVxCellAbsPt.getY());
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
							MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
							objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
							objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
							objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
							objVxCellAnim.play();
						}
					}
				}
			}
			MagnetRONAnimation.waitAnimationEnd();
			if (aliasType.equals(AliasType.CONSTRACTOR_INVOCATION)) {
				sleepMainThread(500L);
			}
			removeCalledMethodExecutionVertex(srcObjVx, methodExec, calledMethodExec);
		} else {
			removeMethodExecutionVertex(srcObjVx, methodExec);
		}
	}

	/**
	 * Remove {@code MethodExecutionVertex} on AliasType is {@code AliasType.METHOD_INVOCATION}.
	 * 
	 * @param sourceObjectVertex
	 * @param methodExecution
	 */
	private void removeMethodExecutionVertex(ObjectVertex sourceObjectVertex, MethodExecution methodExecution) {	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				//  Remove source {@code ObjectVertex} from Locals and Arguments of called {@code MethodExecution}'s Vertex.
				if (methodExecToVertexMap.containsKey(methodExecution)) {
					mxCell dstMethodExecVxCell = (mxCell)methodExecToVertexMap.get(methodExecution).getCell();
					if (!dstMethodExecVxCell.getParent().equals(getMxDefaultParent())) {
						// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
						dstMethodExecVxCell.getParent().remove(dstMethodExecVxCell);
						dstMethodExecVxCell.setParent(getMxDefaultParent());
					}
					mxgraph.removeCells(new Object[] {dstMethodExecVxCell});
					objectToVertexMap.get(methodExecution.getThisObjId()).getMethodExecutionVertices().remove(methodExecToVertexMap.get(methodExecution));
					methodExecToVertexMap.remove(methodExecution);
					edgeMap.remove(methodExecution.getSignature());
					updateObjectVertices();
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
	}

	/**
	 * Remove called {@code MethodExecutionVertex} with alias type {@code AliasType#METHOD_INVOCATION}.
	 * 
	 * @param sourceVertexObject: source object vertex that a called method execution has temporarily
	 * @param methodExec: current method execution
	 * @param calledMethodExec: called method execution
	 */
	protected void removeCalledMethodExecutionVertex(ObjectVertex sourceObjectVertex, MethodExecution methodExecution, MethodExecution calledMethodExecution) {	
		MagnetRONAnimation.waitAnimationEnd();
		
		//  Remove ObjectVertex other than source ObjectVertex from locals and arguments of called MethodExecutionVertex.  
		if (methodExecToVertexMap.containsKey(calledMethodExecution)) {
			MethodExecutionVertex calledMethodExecVx = methodExecToVertexMap.get(calledMethodExecution);

				// TODO: Confirm bug.
				List<ObjectVertex> arguments = new ArrayList<>(calledMethodExecVx.getArguments());
				if (arguments.size() != 0) {
					for (ObjectVertex objVx: arguments) {
						if (objVx != sourceObjectVertex) {
							mxICell objVxCell = (mxICell)objVx.getCell();
							Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxCell);
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									if (!objVxCell.getParent().equals(getMxDefaultParent())) {
										// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
										objVxCell.getParent().remove(objVxCell);
										objVxCell.setParent(getMxDefaultParent());
									}
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
							if (!objVxCellAbsPt.equals(objVx.getInitialPoint())) {
								// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
								mxgraph.getModel().beginUpdate();
								synchronized (mxgraph.getModel()) {
									try {
										objVxCell.getGeometry().setX(objVxCellAbsPt.getX());
										objVxCell.getGeometry().setY(objVxCellAbsPt.getY());
									} finally {
										mxgraph.getModel().endUpdate();
									}
								}
								MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
								objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
								objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
								objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
								objVxCellAnim.syncPlay();
							}
							methodExecToVertexMap.get(calledMethodExecution).getArguments().remove(objVx);
						}
					}
				}
				
				List<ObjectVertex> locals = new ArrayList<>(calledMethodExecVx.getLocals());
				if (locals.size() != 0) {
					for (ObjectVertex objVx: locals) {
						if (objVx != sourceObjectVertex) {
							mxICell objVxCell = (mxICell)objVx.getCell();
							Point2D objVxCellAbsPt = getAbsolutePointforCell(objVxCell);
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									if (!objVxCell.getParent().equals(getMxDefaultParent())) {
										// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
										objVxCell.getParent().remove(objVxCell);
										objVxCell.setParent(getMxDefaultParent());
									}
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
							if (!objVxCellAbsPt.equals(objVx.getInitialPoint())) {
								// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
								mxgraph.getModel().beginUpdate();
								synchronized (mxgraph.getModel()) {
									try {
										objVxCell.getGeometry().setX(objVxCellAbsPt.getX());
										objVxCell.getGeometry().setY(objVxCellAbsPt.getY());
									} finally {
										mxgraph.getModel().endUpdate();
									}
								}
								MagnetRONAnimation objVxCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
								objVxCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
								objVxCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
								objVxCellAnim.init(objVxCell, objVx.getInitialX(), objVx.getInitialY(), threadPoolExecutor);
								objVxCellAnim.syncPlay();
							}
							methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(objVx);
						}
					}
				}
						
			if (methodExecution == null) {
				return;
			}
			
			mxICell srcMethodExecVxCell = (mxICell)methodExecToVertexMap.get(methodExecution).getCell();
			mxICell dstMethodExecVxCell = (mxICell)calledMethodExecVx.getCell();
			
			scrollCellsToVisible(srcMethodExecVxCell.getParent(), dstMethodExecVxCell.getParent());
	
			try {
				Point2D srcMethodExecVxCellAbsPt = null;
				Point2D dstMethodExecVxCellAbsPt = null;
				final mxICell[] cloneDstMethodExecVxCell = new mxICell[1];
				// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
				mxgraph.getModel().beginUpdate();
				synchronized (mxgraph.getModel()) {
					try {
						mxgraph.removeCells(mxgraph.getEdgesBetween(srcMethodExecVxCell, dstMethodExecVxCell));
						srcMethodExecVxCellAbsPt = getAbsolutePointforCell(srcMethodExecVxCell);
						dstMethodExecVxCellAbsPt = getAbsolutePointforCell(dstMethodExecVxCell);
						cloneDstMethodExecVxCell[0] = (mxICell) mxgraph.addCell(dstMethodExecVxCell.clone());
						cloneDstMethodExecVxCell[0].getGeometry().setX(dstMethodExecVxCellAbsPt.getX());
						cloneDstMethodExecVxCell[0].getGeometry().setY(dstMethodExecVxCellAbsPt.getY());
						cloneDstMethodExecVxCell[0].setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
						cloneDstMethodExecVxCell[0].setValue(null);
						mxICell tmpEdgeCell = (mxICell) mxgraph.insertEdge(getMxDefaultParent(), null, null, srcMethodExecVxCell, cloneDstMethodExecVxCell[0]);
						tmpEdgeCell.setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
					} finally {
						mxgraph.getModel().endUpdate();
					}
				}
				
				// Animate an edge to shrink.
				MagnetRONAnimation edgeCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
				edgeCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
				edgeCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
				edgeCellAnim.init(cloneDstMethodExecVxCell[0], srcMethodExecVxCellAbsPt.getX(), srcMethodExecVxCellAbsPt.getY() + srcMethodExecVxCell.getGeometry().getHeight(), threadPoolExecutor);
				edgeCellAnim.setOnFinished(new ActionListener() {
					@Override
					public void actionPerformed(java.awt.event.ActionEvent e) {
						// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
						mxgraph.getModel().beginUpdate();
						synchronized (mxgraph.getModel()) {
							try {
								// Test code (will be deleted)
								System.out.println(TAG + ": Shrink edge animation action performed.");
								mxgraph.removeCells(new Object[]{cloneDstMethodExecVxCell[0]});

								// TODO: Confirm execution order.
								if (!dstMethodExecVxCell.getParent().equals(getMxDefaultParent())) {
									// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
									dstMethodExecVxCell.getParent().remove(dstMethodExecVxCell);
									dstMethodExecVxCell.setParent(getMxDefaultParent());
								}
								mxgraph.removeCells(new Object[] {dstMethodExecVxCell});
								update();
							} finally {
								mxgraph.getModel().endUpdate();
							}
						} 
					}
				});
				edgeCellAnim.play();
							
				if (!calledMethodExecution.isStatic()) {
					objectToVertexMap.get(calledMethodExecution.getThisObjId()).getMethodExecutionVertices().remove(methodExecToVertexMap.get(calledMethodExecution));
				} else {
					// TODO: Confirm why is this object id of the caller method used?
					String objId = calledMethodExecution.getCallerMethodExecution().getThisObjId();
					if (objId.matches("0")) {
						objId += ":" + calledMethodExecution.getCallerMethodExecution().getThisClassName();
					}
					objectToVertexMap.get(objId).getMethodExecutionVertices().remove(methodExecToVertexMap.get(calledMethodExecution));
				}
				methodExecToVertexMap.get(calledMethodExecution).getLocals().remove(sourceObjectVertex);
				methodExecToVertexMap.remove(calledMethodExecution);
				edgeMap.remove(methodExecution.getSignature());						
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
			sleepMainThread(getAnimationDelayMillis());
		}
	}
	
	/**
	 * Create an edge between {@code MethodExecutions} while animating the edge to stretch.
	 */
	private void createEdgesToMethodExecutions() {
		List<MethodExecution> methodExecList = new ArrayList<>(methodExecToVertexMap.keySet());

		// TODO: Fix a bug where an edge orientation is reversed.
		for (int i = 0; i < methodExecList.size() - 1; i++) {
			MethodExecution srcMethodExec = methodExecList.get(i);
			MethodExecution dstMethodExec = methodExecList.get(i + 1);
			String methodSig = srcMethodExec.getSignature();
			
			if (!edgeMap.containsKey(methodSig)) {
				// Draw an edge from sourceVertexCell to destinationVertexCell.
				mxICell srcMethodExecVxCell = (mxICell)methodExecToVertexMap.get(srcMethodExec).getCell();
				mxICell dstMethodExecVxCell = (mxICell)methodExecToVertexMap.get(dstMethodExec).getCell();
				Point2D srcMethodExecVxCellAbsPt = getAbsolutePointforCell(srcMethodExecVxCell);
				Point2D dstMethodExecVxCellAbsPt = getAbsolutePointforCell(dstMethodExecVxCell);

				MagnetRONAnimation.waitAnimationEnd();
				scrollCellsToVisible(srcMethodExecVxCell.getParent(), dstMethodExecVxCell.getParent(), 2);
				
				try {
					final mxICell[] cloneDstMethodExecVxCell = new mxICell[1];
					// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
					mxgraph.getModel().beginUpdate();
					synchronized (mxgraph.getModel()) {
						try {
							cloneDstMethodExecVxCell[0] = (mxICell) mxgraph.addCell(dstMethodExecVxCell.clone());
							cloneDstMethodExecVxCell[0].getGeometry().setX(srcMethodExecVxCellAbsPt.getX());
							cloneDstMethodExecVxCell[0].getGeometry().setY(srcMethodExecVxCellAbsPt.getY() + dstMethodExecVxCell.getGeometry().getHeight());
							cloneDstMethodExecVxCell[0].setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
							cloneDstMethodExecVxCell[0].setValue(null);
							cloneDstMethodExecVxCell[0].setVisible(true);
							mxICell tmpEdgeCell = (mxICell) mxgraph.insertEdge(getMxDefaultParent(), null, null, srcMethodExecVxCell, cloneDstMethodExecVxCell[0]);
							tmpEdgeCell.setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
							dstMethodExecVxCell.setVisible(true);
							update();
						} finally {
							mxgraph.getModel().endUpdate();
						}
					}
					
					// Animate an edge to stretch.
					MagnetRONAnimation edgeCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
					edgeCellAnim.setTotalCycleCount(getMagnetRONAnimationTotalCycleCount());
					edgeCellAnim.setDelay(getMagnetRONAnimationDelayMillis());
					edgeCellAnim.init(cloneDstMethodExecVxCell[0], dstMethodExecVxCellAbsPt.getX(), dstMethodExecVxCellAbsPt.getY(), threadPoolExecutor);
					edgeCellAnim.setOnFinished(new ActionListener() {
						@Override
						public void actionPerformed(java.awt.event.ActionEvent e) {
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									// Test code (will be deleted)
									System.out.println(TAG + ": Stretch edge animation action performed. ");
									mxICell edgeCell = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), methodSig, null, srcMethodExecVxCell, dstMethodExecVxCell);
									if (!edgeCell.getParent().equals(getMxDefaultParent())) {
										// If parent of Edge cell isn't mxDefaltParent, reset parent.
										edgeCell.getParent().remove(edgeCell);
										edgeCell.setParent(getMxDefaultParent());
									}
									mxgraph.orderCells(false, new Object[] {edgeCell});
									edgeCell.setStyle("exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;");
									edgeMap.put(methodSig, new Edge(methodSig, TypeName.Call, edgeCell));
									mxgraph.removeCells(new Object[]{cloneDstMethodExecVxCell[0]});
									update();
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
						}
					});
					edgeCellAnim.play();					
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/** 
	 * Styles all cells on the graph. 
	 */
	protected void setCellsStyle() {
		List<Object> objVxs = new ArrayList<>();
		List<Object> staticObjVxs = new ArrayList<>();
		List<Object> alignMidObjVxs = new ArrayList<>();
		List<Object> alignTopObjVxs = new ArrayList<>();
		List<Object> refEdges = new ArrayList<>();
		List<Object> refCreateEdges = new ArrayList<>();
		List<Object> methodExecEdges = new ArrayList<>();
		List<Object> roundEdges = new ArrayList<>();
	
		for (Entry<String, ObjectVertex> objectToVertexEntry: objectToVertexMap.entrySet()) {
			String objId = objectToVertexEntry.getKey();
			ObjectVertex objVx = objectToVertexEntry.getValue();
			if (objId.startsWith("0:")) {
				staticObjVxs.add(objVx.getCell());
			} else {
				objVxs.add(objVx.getCell());
			}
			if(objVx.getMethodExecutionVertices().size() == 0) {
				alignMidObjVxs.add(objVx.getCell());
			} else {
				alignTopObjVxs.add(objVx.getCell());
			}
		}
	
		List<MethodExecutionVertex> methodExecVxList = new ArrayList<>(methodExecToVertexMap.values());
		Collections.reverse(methodExecVxList);
		for (int i = 0; i < methodExecVxList.size(); i++) {
			mxICell methodExecVxCell = (mxICell) methodExecVxList.get(i).getCell();
			if (i == 0) {
				methodExecVxCell.setStyle("fillColor=#ff7fbf");
			} else if (i == 1) {
				methodExecVxCell.setStyle("fillColor=#ff99cc");				
			} else if (i == 2) {
				methodExecVxCell.setStyle("fillColor=#ffb2d8");				
			} else if (i == 3) {
				methodExecVxCell.setStyle("fillColor=#ffcce5");				
			} else if (i == 4) {
				methodExecVxCell.setStyle("fillColor=#ffe0ef");				
			} else {
				break;
			}
		}
	
		for (Edge edge: edgeMap.values()) {
			roundEdges.add(edge.getCell());
			switch(edge.getTypeName()) {
			case Reference:
				refEdges.add(edge.getCell());
				break;
			case Create:
				refEdges.add(edge.getCell());
				refCreateEdges.add(edge.getCell());
				break;
			case Call:
				methodExecEdges.add(edge.getCell());
				break;
			default:
				break;
			}
		}
	
		// Styles ObjectVertex.
		mxgraph.setCellStyles(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE, objVxs.toArray(new Object[objVxs.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE, objVxs.toArray(new Object[objVxs.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_UNDERLINE, true, objVxs.toArray(new Object[objVxs.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, alignMidObjVxs.toArray(new Object[alignMidObjVxs.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, alignTopObjVxs.toArray(new Object[alignTopObjVxs.size()]));
		// Styles Edge.
		mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM, refEdges.toArray(new Object[refEdges.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, refCreateEdges.toArray(new Object[refCreateEdges.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_ROUNDED, 1, true, roundEdges.toArray(new Object[roundEdges.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, roundEdges.toArray(new Object[roundEdges.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_BOTTOM, roundEdges.toArray(new Object[roundEdges.size()]));
		// Styles MethodExecutionVertex.		
		mxgraph.setCellStyles(mxConstants.STYLE_STROKECOLOR, "#008000", methodExecEdges.toArray(new Object[methodExecEdges.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, methodExecEdges.toArray(new Object[methodExecEdges.size()]));
	}

	protected Point2D getAbsolutePointforCell(mxICell cell) {
		Point2D p1 = new Point2D.Double(cell.getGeometry().getX(), cell.getGeometry().getY());;
		if(cell.getParent() == null 
				|| cell.getParent().getValue() == null
				|| cell.equals(cell.getParent())) {
			return p1;
		}
		Point2D p2 = getAbsolutePointforCell(cell.getParent());
		return new Point2D.Double(p1.getX() + p2.getX(), p1.getY() + p2.getY());
	}

	/**
	 *  Update the graph on the JFrame by styling the cells and refreshing the mxgraphComponent. 
	 */
	protected void update() {
		/* Given a cell, we can change it's style attributes, for example the color. 
		 * NOTE that you have to call the graphComponent.refresh() function, 
		 * otherwise you won't see the difference! */
		setCellsStyle();
		getGraphComponent().refresh();
	}
	
	protected void scrollCellToVisible(mxICell cell, boolean center) {
		if (isAutoTracking()) {
			getGraphComponent().scrollCellToVisible(cell, center);
		}		
	}
	
	private void scrollCellsToVisible(mxICell cell1, mxICell cell2) {
		scrollCellsToVisible(cell1, cell2, 1);
	}
	
	private void scrollCellsToVisible(mxICell cell1, mxICell cell2, int priorityArgumentsIndex) {
		if (isAutoTracking()) {
			Point2D p1 = getAbsolutePointforCell(cell1);
			Point2D p2 = getAbsolutePointforCell(cell2);
			if (p1.getX() <= p2.getX()) {
				if (p1.getY() <= p2.getY()) {
					double p2X = p2.getX() + cell2.getGeometry().getWidth();
					double p2Y = p2.getY() + cell2.getGeometry().getHeight();
					p2.setLocation(p2X, p2Y);
				} else {
					double p1Y = p1.getY() + cell1.getGeometry().getHeight();
					double p2X = p2.getX() + cell2.getGeometry().getWidth();
					p1.setLocation(p1.getX(), p1Y);
					p2.setLocation(p2X, p2.getY());
				}
			} else {
				if (p1.getY() <= p2.getY()) {
					double p1X = p1.getX() + cell1.getGeometry().getWidth();
					double p2Y = p2.getY() + cell2.getGeometry().getHeight();
					p1.setLocation(p1X, p1.getY());
					p2.setLocation(p2.getX(), p2Y);
				} else {
					double p1X = p1.getX() + cell1.getGeometry().getWidth();
					double p1Y = p1.getY() + cell1.getGeometry().getHeight();
					p1.setLocation(p1X, p1Y);
				}
			}
			scrollPointsToVisible(p1, p2, false, priorityArgumentsIndex);
		}
	}
	
	private void scrollCellAndPointToVisible(mxICell cell1, Point2D p2, int priorityArgumentsIndex) {
		if (isAutoTracking()) {
			Point2D p1 = getAbsolutePointforCell(cell1);
			if (p1.getX() <= p2.getX()) {
				if (p1.getY() <= p2.getY()) {
					double p2X = p2.getX() + DEFAULT_OBJECT_VERTEX_SIZE.getWidth();
					double p2Y = p2.getY() + DEFAULT_OBJECT_VERTEX_SIZE.getHeight();
					p2.setLocation(p2X, p2Y);
				} else {
					double p1Y = p1.getY() + cell1.getGeometry().getHeight();
					double p2X = p2.getX() + DEFAULT_OBJECT_VERTEX_SIZE.getWidth();
					p1.setLocation(p1.getX(), p1Y);
					p2.setLocation(p2X, p2.getY());
				}
			} else {
				if (p1.getY() <= p2.getY()) {
					double p1X = p1.getX() + cell1.getGeometry().getWidth();
					double p2Y = p2.getY() + DEFAULT_OBJECT_VERTEX_SIZE.getHeight();
					p1.setLocation(p1X, p1.getY());
					p2.setLocation(p2.getX(), p2Y);
				} else {
					double p1X = p1.getX() + cell1.getGeometry().getWidth();
					double p1Y = p1.getY() + cell1.getGeometry().getHeight();
					p1.setLocation(p1X, p1Y);
				}
			}
			scrollPointsToVisible(p1, p2, false, priorityArgumentsIndex);
		}
	}

	/**
	 * 
	 * 
	 * @param p1
	 * @param p2
	 * @param center
	 * @param priorityArgumentsIndex: 1 is the default value, indicating that 1st argument(p1) of this method should be preferred.
	 */
	private void scrollPointsToVisible(Point2D p1, Point2D p2, boolean center, int priorityArgumentsIndex) {
		if (isAutoTracking()) {
			Rectangle rec = new Rectangle();
			int p1X = (int) p1.getX(), p1Y = (int) p1.getY();
			int p2X = (int) p2.getX(), p2Y = (int) p2.getY();
			if (p1X <= p2X) {
				if (p1Y <= p2Y) {
					rec.setBounds(p1X, p1Y, p2X - p1X, p2Y - p1Y);
					if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
						switch(priorityArgumentsIndex) {
							case 1:
								break;
							case 2:
								int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
								int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
								rec.setLocation(x, y);
								break;
						}
						rec.setSize(super.getWidth(), super.getHeight());
					}
				} else {
					rec.setBounds(p1X, p2Y, p2X - p1X, p1Y - p2Y);
					if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
						switch(priorityArgumentsIndex) {
							case 1:
								int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
								rec.setLocation((int) rec.getX(), y);
								break;
							case 2:
								int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
								rec.setLocation(x, (int) rec.getY());
								break;
						}
						rec.setSize(super.getWidth(), super.getHeight());
					}
				}
			} else {
				if (p1Y <= p2Y) {
					rec.setBounds(p2X, p1Y, p1X - p2X, p2Y - p1Y);
					if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
						switch(priorityArgumentsIndex) {
							case 1:
								int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
								rec.setLocation(x, (int) rec.getY());
								break;
							case 2:
								int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
								rec.setLocation((int) rec.getX(), y);
								break;
						}
						rec.setSize(super.getWidth(), super.getHeight());
					}
				} else {
					rec.setBounds(p2X, p2Y, p1X - p2X, p1Y - p2Y);
					if (rec.getWidth() > super.getWidth() || rec.getHeight() > super.getHeight()) {
						switch(priorityArgumentsIndex) {
							case 1:
								int x = (int) (rec.getX() + (rec.getWidth() - super.getWidth()));
								int y = (int) (rec.getY() + (rec.getHeight() - super.getHeight()));
								rec.setLocation(x, y);																
								break;
							case 2:
								break;
						}
						rec.setSize(super.getWidth(), super.getHeight());
					}
				}
			}
			
			if (center) {
				int x = (int) (rec.getCenterX() - super.getWidth() / 2);
				int y = (int) (rec.getCenterY() - super.getHeight() / 2);
				rec.setBounds(x, y, super.getWidth(), super.getHeight());
			}
			
			scrollRectToVisible(rec);
		}
	}

	@Override
	public void scrollRectToVisible(Rectangle rec) {
		Rectangle visibleRec = getGraphComponent().getGraphControl().getVisibleRect();
		if (isAutoTracking() && !rec.contains(visibleRec)) {
			System.out.println(TAG + ": Before scroll visibleRect=" + getGraphComponent().getGraphControl().getVisibleRect());
			getGraphComponent().getGraphControl().scrollRectToVisible(rec);
			System.out.println(TAG + ": After scroll visibleRect=" + getGraphComponent().getGraphControl().getVisibleRect());
		}
	}

	private boolean isAutoTracking() {
		return this.fAutoTracking;
	}

	public void setAutoTracking(boolean fAutoTracking) {
		if (fAutoTracking != isAutoTracking()) {
			this.fAutoTracking = fAutoTracking;
		}
	}
	
	protected void setCurrentFrame(int numberFrame) {
		this.curFrame = numberFrame;
	}

	public void setAnimationSpeed(double animationSpeed) {
		this.animationSpeed = animationSpeed;
	}

	public double getAnimationSpeed() {
		return animationSpeed;
	}

	protected long getAnimationDelayMillis() {
		return (long) (animationDelayMillis / getAnimationSpeed());
	}

	protected int getMagnetRONAnimationTotalCycleCount() {
		return (int) (magnetRONAnimationTotalCycleCount / getAnimationSpeed());
	}

	protected long getMagnetRONAnimationDelayMillis() {
		return (long) (magnetRONAnimationDelayMillis / getAnimationSpeed());
	}

	protected static String[] formatFieldName(String fieldName) {
		String fieldNames[] = fieldName.split("\\.");
		String names[] = new String[] {fieldNames[0], fieldNames[fieldNames.length - 1]};
		for(int i = 1; i < fieldNames.length - 1; i++) {
			names[0] += "." + fieldNames[i];
		}
		return names;
	}

	protected String formatMethodSignature(String methodSignature, String thisClassName) {
		// TODO: Modify algorithm formatMethodSignature().
		// Step1 : split "("
		methodSignature = methodSignature.substring(0, methodSignature.lastIndexOf('('));
		// Step2 : split " "
		String[] methodSigs = methodSignature.split(" ");
		String tmpMethodSig = methodSigs[methodSigs.length-1];
		// Step3 : split "."
		String[] thisClassNames = thisClassName.split("\\.");
		methodSigs = tmpMethodSig.split("\\.");
		StringBuffer sb = new StringBuffer();
		int i = methodSigs.length - 2;
		int count = methodSignature.split("\\(").length - 1;
		if (count > 0) i -= count;
		if (i >= 0 && !thisClassNames[thisClassNames.length - 1].equals(methodSigs[i])) {
			if (thisClassNames[thisClassNames.length - 1].equals(methodSigs[i + 1])) i += 1;
			sb.append(methodSigs[i]);
			if (methodSigs.length - i > 1) sb.append(".");
		}
		for (i = i + 1; i < methodSigs.length; i++) {
			sb.append(methodSigs[i]);
			if (methodSigs.length - i > 1) sb.append(".");
		}
		sb.append("()");
		
		String newMethodSignature = sb.toString();
		if (!newMethodSignature.isEmpty()) {
			return newMethodSignature;			
		}
		return methodSignature;
	}

	protected String formatArrayName(String srcClassName) {
		// Step1 : remove "[L"
		StringBuffer sb = new StringBuffer();
		sb.append(srcClassName.substring(2, srcClassName.length()-1));
		sb.append("[]");
		return sb.toString();		
	}

	protected String formatArrayIndex(int index) {
		StringBuffer sb = new StringBuffer();
		sb.append("[");
		sb.append(index);
		sb.append("]");
		return sb.toString();
	}

	/**
	 * Test code (will be deleted)
	 */
	protected void outputLog() {
		for (Object obj: mxgraph.getChildCells(getMxDefaultParent())) {
			System.out.println(obj + " " + obj.hashCode());
			for (int i = 0; i < ((mxICell)obj).getChildCount(); i++) {
				System.out.println("   " + ((mxICell)obj).getChildAt(i) + " " + obj.hashCode());
			}
		}
		System.out.println("\nObject");
		for (Entry<String, ObjectVertex> e: objectToVertexMap.entrySet()) {
			String objId = e.getKey();
			ObjectVertex vo = e.getValue();
			if (vo.getCell() != null) {
				System.out.println(vo.getLabel() + " (" + objId + ")" + " " + vo.getCell().hashCode());				
			} else {
				System.out.println(vo.getLabel() + " (" + objId + ")");								
			}
			for (MethodExecutionVertex vme: vo.getMethodExecutionVertices()) {
				System.out.println("   " + vme.getLabel());
				for (ObjectVertex vmevo: vme.getArguments()) {
					System.out.println("      Argument: " + vmevo.getLabel());					
				}
				for (ObjectVertex vmevo: vme.getLocals()) {
					System.out.println("      Local: " + vmevo.getLabel());					
				}
			}
		}
		System.out.println("\nEdge");
		for (Edge e: edgeMap.values()) {
			System.out.println(e.getLabel() + "(" + ((mxICell)e.getCell()).getId() + ")");
			if (((mxICell)e.getCell()).getParent() != null) {
				System.out.println(" " + ((mxICell)e.getCell()).getParent().getId());
			}
		}
	}
	
	/**
	 * Whether parents of source mxICell contain destination mxICell.
	 * 
	 * @param sourceCell
	 * @param destinationCell
	 * @return
	 */
	private boolean isParent(mxICell sourceCell, mxICell destinationCell) {
		mxICell srcParentCell = sourceCell.getParent();
		if (srcParentCell == null || srcParentCell.getValue() == null || destinationCell == null) {
			return false;
		}
		if (srcParentCell.equals(destinationCell)) {
			return true;
		}
		return isParent(srcParentCell, destinationCell);
	}

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