Newer
Older
MagnetRON / src / org / ntlab / deltaViewer / MagnetRONViewer.java
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.Trace;
import org.ntlab.trace.TracePoint;

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

/**

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

	private static final long serialVersionUID = -6828987937804142956L;

	// Test code (will be deleted)
	private static final String TAG = MagnetRONViewer.class.getSimpleName();

	protected static Dimension DEFAULT_COMPONENT_SIZE = new Dimension(1300, 700);

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

	protected static long DEFAULT_THREAD_SLEEP_MILLIS = 1100L;
	protected static long POSTPONE_ANIMATION_MILLIS = 250L;

	protected IAliasCollector aliasCollector;

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


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

	protected ThreadPoolExecutor threadPoolExecutor;
	protected int curFrame = 0;

	private boolean autoTracking = 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 objectVertex = objectToVertexMap.get(alias.getObjectId());
		mxICell ovCell = 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 srcCell = (mxICell)objectToVertexMap.get(srcObjId).getCell();
		double srcWidth = srcCell.getGeometry().getWidth();
		double srcHeight = srcCell.getGeometry().getHeight();
		double overlapWidth = srcWidth * Math.sqrt(2) * 0.1;
		double overlapHeight = srcHeight  - (srcHeight * Math.sqrt(2) * 0.1);
		Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
				
		MethodInvocation methodInv;
		String fieldName = null;
		if (!methodExec.isCollectionType() 
				&& alias.getOccurrencePoint().getStatement() != null) {
			methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
			fieldName = methodInv.getCallerSideMethodName();
		}
		
		MagnetRONAnimation.waitAnimationEnd();
		scrollPointsToVisible(srcCellAbsPt, objectVertex.getInitialPoint(), true);

		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				// Creates a white mxICell of ObjectVertex. 
				ovCell = 
						(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), alias.getObjectId(), objectVertex.getLabel(), 
								srcCellAbsPt.getX() + overlapWidth, srcCellAbsPt.getY() + overlapHeight, 
								DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(), 
								"fillColor=white");
				objectVertex.setCell(ovCell);
				mxICell edge = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcCell, ovCell);
				edgeMap.put(alias.getObjectId() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
				for (String edgeKey: edgeMap.keySet()) {
					Edge e = edgeMap.get(edgeKey);
					if (edgeKey.startsWith(alias.getObjectId()  + ".") && e.getTypeName() == Edge.TypeName.PreReference) {
						mxICell dstCell =  (mxICell) objectToVertexMap.get(e.getDstObjId()).getCell();
						String fieldNames[] = edgeKey.split(".");
						mxICell edge2 = null;
						if (fieldNames.length < 2) {
							edge2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), "", "", ovCell, dstCell);
						} else {
							edge2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldNames[1], fieldNames[1], ovCell, dstCell);
						}
						Point2D absPtSrcCell = getAbsolutePointforCell((mxICell)ovCell);
						Point2D absPtDstCell = getAbsolutePointforCell((mxICell)dstCell);
						setEdgePoint((mxICell)edge2, absPtSrcCell, absPtDstCell);
						e.setTypeName(Edge.TypeName.Reference);
						e.setCell(edge2);
					}
				}
				update();
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		MagnetRONAnimation ovCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		ovCellAnim.setTotalCycleCount(10);
		ovCellAnim.setDelay(100);
		ovCellAnim.init(ovCell, objectVertex.getInitialX(), objectVertex.getInitialY(), threadPoolExecutor);
		ovCellAnim.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 objectVertex = objectToVertexMap.get(alias.getObjectId()); // Create mxICell of this ObjectVertex.
		MethodExecution methodExec = alias.getMethodExecution();
		mxICell ovCell = null;
		
		// Position of srcCell is start point for ovCell to TranslateAnimation.
		String srcObjId = methodExec.getArguments().get(0).getId();
		mxICell srcCell = (mxICell)objectToVertexMap.get(srcObjId).getCell();
		double srcWidth = srcCell.getGeometry().getWidth();
		double srcHeight = srcCell.getGeometry().getHeight();
		double overlapWidth = srcWidth * Math.sqrt(2) * 0.1;
		double overlapHeight = srcHeight  - (srcHeight * Math.sqrt(2) * 0.1);
		Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
		
		scrollPointsToVisible(srcCellAbsPt, objectVertex.getInitialPoint(), true);

		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. 
				ovCell = 
						(mxICell) mxgraph.insertDeltaVertex(getMxDefaultParent(), objectVertex.getLabel(), objectVertex.getLabel(), 
								srcCellAbsPt.getX() + overlapWidth, srcCellAbsPt.getY() + overlapHeight, 
								DEFAULT_OBJECT_VERTEX_SIZE.getWidth(), DEFAULT_OBJECT_VERTEX_SIZE.getHeight(), 
								"fillColor=white");
				objectVertex.setCell(ovCell);
				mxICell edge = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldName, null, srcCell, ovCell);
				edgeMap.put(alias.getObjectId() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
				for (String edgeKey: edgeMap.keySet()) {
					Edge e = edgeMap.get(edgeKey);
					if (edgeKey.startsWith(alias.getObjectId()  + ".") && e.getTypeName() == Edge.TypeName.PreReference) {
						mxICell dstCell =  (mxICell) objectToVertexMap.get(e.getDstObjId()).getCell();
						String fieldNames[] = edgeKey.split(".");
						mxICell edge2 = null;
						if (fieldNames.length < 2) {
							edge2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), "", "", ovCell, dstCell);
						} else {
							edge2 = (mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldNames[1], fieldNames[1], ovCell, dstCell);
						}
						Point2D absPtSrcCell = getAbsolutePointforCell((mxICell)ovCell);
						Point2D absPtDstCell = getAbsolutePointforCell((mxICell)dstCell);
						setEdgePoint((mxICell)edge2, absPtSrcCell, absPtDstCell);
						e.setTypeName(Edge.TypeName.Reference);
						e.setCell(edge2);
					}
				}
				update();
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		MagnetRONAnimation ovCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		ovCellAnim.setTotalCycleCount(10);
		ovCellAnim.setDelay(100);
		ovCellAnim.init(ovCell, objectVertex.getInitialX(), objectVertex.getInitialY(), threadPoolExecutor);
		ovCellAnim.syncPlay();
	}

	/**
	 * Create edge of {@code ObjectReference}.
	 * 
	 * @param fieldUpdateStatement
	 * @param fieldName
	 */
	protected void createObjectRefrence(FieldUpdate fieldUpdateStatement, String fieldName) {
		// Create edge between srcCell and dstCell.
		String srcObjId = fieldUpdateStatement.getContainerObjId();
		mxICell srcCell = (mxICell)objectToVertexMap.get(srcObjId).getCell();
		Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
		
		String dstObjId = fieldUpdateStatement.getValueObjId();
		ObjectVertex dstObjectVertex = objectToVertexMap.get(dstObjId);
		mxICell dstCell = (mxICell)dstObjectVertex.getCell();
		Point2D dstCellAbsPt = getAbsolutePointforCell(dstCell);

		scrollPointsToVisible(srcCellAbsPt, dstObjectVertex.getInitialPoint(), true);
		
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if (!dstCell.getParent().equals(getMxDefaultParent())) {
					// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
					dstCell.getParent().remove(dstCell);
					dstCell.setParent(getMxDefaultParent());
				}
				dstCell.getGeometry().setX(dstCellAbsPt.getX());
				dstCell.getGeometry().setY(dstCellAbsPt.getY());
				mxICell edge = 
						(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), fieldUpdateStatement.getFieldName(), fieldName, 
								srcCell, dstCell);
				edge.setStyle("strokeColor=red;");
//				mxgraph.orderCells(true, new Object[] {edge});
				edgeMap.put(fieldUpdateStatement.getContainerObjId(), new Edge(fieldName, TypeName.Reference, edge));
			} finally {
				mxgraph.getModel().endUpdate();
			}			
		}
		MagnetRONAnimation dstCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		dstCellAnim.setTotalCycleCount(10);
		dstCellAnim.setDelay(100);
		dstCellAnim.init(dstCell, dstObjectVertex.getInitialX(), dstObjectVertex.getInitialY(), threadPoolExecutor);
		dstCellAnim.syncPlay();
		
		// If the animation didn't work to the end.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				dstCell.getGeometry().setX(dstObjectVertex.getInitialX());
				dstCell.getGeometry().setY(dstObjectVertex.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 srcCell and dstCell.
		mxICell srcCell = (mxICell)objectToVertexMap.get(sourceObjectId).getCell();
		Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);

		ObjectVertex dstObjectVertex = objectToVertexMap.get(destinationObjectId);
		mxICell dstCell = (mxICell) dstObjectVertex.getCell();
		Point2D dstCellAbsPt = getAbsolutePointforCell(dstCell);

		scrollPointsToVisible(srcCellAbsPt, dstObjectVertex.getInitialPoint(), true);

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if (!dstCell.getParent().equals(getMxDefaultParent())) {
					// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
					dstCell.getParent().remove(dstCell);
					dstCell.setParent(getMxDefaultParent());
				}
				dstCell.getGeometry().setX(dstCellAbsPt.getX());
				dstCell.getGeometry().setY(dstCellAbsPt.getY());
				mxICell edge = 
						(mxICell) mxgraph.insertDeltaEdge(getMxDefaultParent(), destinationObjectId, null,
								srcCell, dstCell);
				edge.setStyle("strokeColor=red;");
				edgeMap.put(destinationObjectId, new Edge(null, TypeName.Reference, edge));
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
		MagnetRONAnimation dstCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		dstCellAnim.setTotalCycleCount(10);
		dstCellAnim.setDelay(100);
		dstCellAnim.init(dstCell, dstObjectVertex.getInitialX(), dstObjectVertex.getInitialY(), threadPoolExecutor);
		dstCellAnim.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 {
				dstCell.getGeometry().setX(dstObjectVertex.getInitialX());
				dstCell.getGeometry().setY(dstObjectVertex.getInitialY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
	}

	/**
	 * Move to position of destination {@code ObjectVertex} from source {@code ObjectVertex}.
	 * 
	 * @param alias
	 */
	protected void moveObjectVertex(Alias alias) {
		// source {@code ObjectVertex}
		String objId = alias.getObjectId();
		ObjectVertex srcObjectVertex = objectToVertexMap.get(objId);
		MethodExecution methodExec = alias.getMethodExecution();
		if (!methodExecToVertexMap.containsKey(methodExec) && methodExec.isStatic()) {
			createMethodExecutionVertex(objId, methodExec.getSignature(), methodExec);
			sleepMainThread(POSTPONE_ANIMATION_MILLIS);
		}
		// destination {@code ObjectVertex}
		MethodExecutionVertex dstMethodExecVertex = methodExecToVertexMap.get(methodExec);
		moveObjectVertex(alias, srcObjectVertex, 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();
		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 srcObjectVertexCell = (mxICell)sourceObjectVertex.getCell();
		mxICell dstMethodExecVertexCell = (mxICell) destinationMethodExecutionVertex.getCell();

		if (srcObjectVertexCell.equals(dstMethodExecVertexCell.getParent())) {
			return;
		}

		scrollCellsToVisible(srcObjectVertexCell, dstMethodExecVertexCell);

		// Remove source ObjectVertex from Locals and Arguments of caller MethodExecutionVertex.  
		if (methodExecToVertexMap.containsKey(callerMethodExecution)) {
			MethodExecutionVertex callerMethodExecVertex = methodExecToVertexMap.get(callerMethodExecution);
			if (callerMethodExecVertex.getLocals().contains(sourceObjectVertex)) {
				callerMethodExecVertex.getLocals().remove(sourceObjectVertex);
			}
			if (callerMethodExecVertex.getArguments().contains(sourceObjectVertex)) {
				callerMethodExecVertex.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 srcCellCoordX = srcObjectVertexCell.getGeometry().getX();
				double srcCellCoordY = srcObjectVertexCell.getGeometry().getY();

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

				Point2D dstCellAbsPt = getAbsolutePointforCell(srcObjectVertexCell.getParent());
				srcObjectVertexCell.getGeometry().setX(srcCellCoordX - dstCellAbsPt.getX());
				srcObjectVertexCell.getGeometry().setY(srcCellCoordY - dstCellAbsPt.getY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}

		int sizeScale = destinationMethodExecutionVertex.getLocals().size();
		double srcCellWidth = srcObjectVertexCell.getGeometry().getWidth();
		double dstCellHeight = dstMethodExecVertexCell.getGeometry().getHeight();
		double srcCellDstX = dstMethodExecVertexCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(2.5)) + (srcCellWidth * sizeScale);
		double srcCellDstY = dstMethodExecVertexCell.getGeometry().getY() + dstCellHeight;

		MagnetRONAnimation srcCellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		srcCellAnim.setTotalCycleCount(10);
		srcCellAnim.setDelay(100);
		srcCellAnim.init(srcObjectVertexCell, srcCellDstX, srcCellDstY, threadPoolExecutor);
		srcCellAnim.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 (!srcObjectVertexCell.getParent().equals(dstMethodExecVertexCell.getParent())) {
//					srcObjectVertexCell.getParent().remove(srcObjectVertexCell);
//					srcObjectVertexCell.setParent(dstMethodExecVertexCell.getParent());
//					dstMethodExecVertexCell.getParent().insert(srcObjectVertexCell);
//				}
				srcObjectVertexCell.getGeometry().setX(dstMethodExecVertexCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(2.5)) + (srcCellWidth * sizeScale));
				srcObjectVertexCell.getGeometry().setY(dstMethodExecVertexCell.getGeometry().getY() + dstCellHeight);
				destinationMethodExecutionVertex.getLocals().add(sourceObjectVertex);
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}
	}

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

		//  Remove source {@code VertexObject} from Locals and Arguments of {@code MethodExecution}'s Vertex.  
		MethodExecution callerMethodExec = methodExecution.getCallerMethodExecution();
		if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getLocals().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(callerMethodExec).getLocals().remove(sourceObjectVertex);
		}

		if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getArguments().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(callerMethodExec).getArguments().remove(sourceObjectVertex);
		}

		int time = destinationMethodExecutionVertex.getArguments().size();
		double srcCoordX = srcCell.getGeometry().getX();
		double srcCoordY = srcCell.getGeometry().getY();

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

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

			double srcCellWidth = srcCell.getGeometry().getWidth();
			double srcCellHeight = srcCell.getGeometry().getHeight();
			double overlapWidth = srcCellWidth - (srcCellWidth * Math.sqrt(2) * 0.1);
			double overlapHeight = srcCellHeight - (srcCellHeight * Math.sqrt(2) * 0.1);

			MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
			vertexAnim.setTotalCycleCount(10);
			vertexAnim.setDelay(100);
			vertexAnim.init(srcCell, 
					dstCell.getGeometry().getX() - overlapWidth, dstCell.getGeometry().getY()  - overlapHeight + (srcCellHeight * time), 
					threadPoolExecutor);
			sleepMainThread(POSTPONE_ANIMATION_MILLIS);
			vertexAnim.syncPlay();

			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			synchronized (mxgraph.getModel()) {
				try {
					srcCell.getGeometry().setX(dstCell.getGeometry().getX() - overlapWidth);
					srcCell.getGeometry().setY(dstCell.getGeometry().getY() - overlapHeight + (srcCellHeight * time));
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}

			destinationMethodExecutionVertex.getArguments().add(sourceObjectVertex);
		} else { // TODO: 仕様上のバグ、ループが発生
			// 元の ObjectVertex
			Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
			Point2D dstParentCellAbsPt = getAbsolutePointforCell(dstCell.getParent());

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

					dstCell.getParent().getGeometry().setX(dstParentCellAbsPt.getX());
					dstCell.getParent().getGeometry().setY(dstParentCellAbsPt.getY());
					srcCell.getGeometry().setX(srcCellAbsPt.getX() - dstParentCellAbsPt.getX());
					srcCell.getGeometry().setY(srcCellAbsPt.getY() - dstParentCellAbsPt.getY());
					srcCell.setStyle("opacity=50;shape=ellipse");
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}

			double srcCellWidth = srcCell.getGeometry().getWidth();
			double srcCellHeight = srcCell.getGeometry().getHeight();
			double overlapWidth = srcCellWidth - (srcCellWidth * Math.sqrt(2) * 0.1);
			double overlapHeight = srcCellHeight - (srcCellHeight * Math.sqrt(2) * 0.1);

			MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
			vertexAnim.setTotalCycleCount(10);
			vertexAnim.setDelay(100);
			vertexAnim.init(srcCell, 
					dstCell.getGeometry().getX() - overlapWidth + (srcCellWidth * time), dstCell.getGeometry().getY()  - overlapHeight + (srcCellHeight * time), 
					threadPoolExecutor);
			vertexAnim.syncPlay();

			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			synchronized (mxgraph.getModel()) {
				try {
					srcCell.getGeometry().setX(dstCell.getGeometry().getX() - overlapWidth + (srcCellWidth * time));
					srcCell.getGeometry().setY(dstCell.getGeometry().getY()  - overlapHeight + (srcCellHeight * time));
				} finally {
					mxgraph.getModel().endUpdate();
				}
			}

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

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

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

		if (srcCell.equals(dstCell.getParent())) {
			System.out.println("Nothing to moveActualArgumentObjectVertex().");
			return;
		}

		//  Remove sourceVertex from Locals and Arguments of MethodExecution's Vertex. 
		if (methodExecToVertexMap.containsKey(methodExecution) && methodExecToVertexMap.get(methodExecution).getLocals().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(methodExecution).getLocals().remove(sourceObjectVertex);
		}

		if (methodExecToVertexMap.containsKey(methodExecution) && methodExecToVertexMap.get(methodExecution).getArguments().contains(sourceObjectVertex)) {
			methodExecToVertexMap.get(methodExecution).getArguments().remove(sourceObjectVertex);
		}

		int time = destinationMethodExecutionVertex.getLocals().size();
		double srcCellCoordX = srcCell.getGeometry().getX();
		double srcCellCoordY = srcCell.getGeometry().getY();

		MagnetRONAnimation.waitAnimationEnd();
		
		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				if(srcCell.getParent().getValue() != null) {
					Point2D srcCellAbsPt = getAbsolutePointforCell(srcCell);
					srcCellCoordX = srcCellAbsPt.getX();
					srcCellCoordY = srcCellAbsPt.getY();
					srcCell.getParent().remove(srcCell);
				}

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

		double srcCellWidth = srcCell.getGeometry().getWidth();
		double dstCellHeight = dstCell.getGeometry().getHeight();
		Point2D srcCellDstPoint = new Point2D.Double(dstCell.getGeometry().getX() - (srcCellWidth / Math.sqrt(3)) + (srcCellWidth * time), 
				dstCell.getGeometry().getY() + dstCellHeight);
		MagnetRONAnimation vertexAnim = new TranslateAnimation(mxgraph, getGraphComponent());
		vertexAnim.setTotalCycleCount(10);
		vertexAnim.setDelay(100);
		vertexAnim.init(srcCell, srcCellDstPoint.getX(), srcCellDstPoint.getY(), threadPoolExecutor);
		vertexAnim.syncPlay();

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				srcCell.getGeometry().setX(srcCellDstPoint.getX());
				srcCell.getGeometry().setY(srcCellDstPoint.getY());
			} finally {
				mxgraph.getModel().endUpdate();
			}
		}

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

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

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

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

	abstract protected void createMethodExecutionVertex(Alias alias);

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

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

		if (methodExecution.isStatic() && !objectId.startsWith("0")) {		// Check the object id is a formal parameter's one.
			objectId = methodExecution.getThisObjId();
			if (objectId.matches("0")) {
				objectId += ":" + methodExecution.getThisClassName();
			}
		}

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

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

	/**
	 * Remove {@code MethodExecutionVertex} on {@code {@link Alias#getAliasType()} is {@code AliasType.METHOD_INVOCATION}.
	 * 
	 * @param alias
	 */
	private void removeMethodExecutionVertex(Alias alias) {
		// source ObjectVertex
		ObjectVertex srcObjectVertex = 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 vo: arguments) {
					// TODO: Implement equals().
					if (vo != srcObjectVertex) {
						mxICell cell = (mxICell)vo.getCell();
						if (!cell.getParent().equals(getMxDefaultParent())) {
							// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									Point2D cellAbsPt = getAbsolutePointforCell(cell);
									cell.getParent().remove(cell);
									cell.setParent(getMxDefaultParent());
									cell.getGeometry().setX(cellAbsPt.getX());
									cell.getGeometry().setY(cellAbsPt.getY());
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
							MagnetRONAnimation cellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
							cellAnim.setTotalCycleCount(10);
							cellAnim.setDelay(100);
							cellAnim.init(cell, vo.getInitialX(), vo.getInitialY(), threadPoolExecutor);
							cellAnim.play();
							methodExecToVertexMap.get(calledMethodExec).getArguments().remove(vo);
						}
					}
				}
				if (locals.size() != 0) {
					for (ObjectVertex vo: locals) {
						if (vo != srcObjectVertex) {
							mxICell cell = (mxICell) vo.getCell();
							// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
							mxgraph.getModel().beginUpdate();
							synchronized (mxgraph.getModel()) {
								try {
									Point2D cellAbsPt = getAbsolutePointforCell(cell);
									if (!cell.getParent().equals(getMxDefaultParent())) {
										// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
										cell.getParent().remove(cell);
										cell.setParent(getMxDefaultParent());
									}
									cell.getGeometry().setX(cellAbsPt.getX());
									cell.getGeometry().setY(cellAbsPt.getY());
								} finally {
									mxgraph.getModel().endUpdate();
								}
							}
							MagnetRONAnimation cellAnim = new TranslateAnimation(mxgraph, getGraphComponent());
							cellAnim.setTotalCycleCount(10);
							cellAnim.setDelay(100);
							cellAnim.init(cell, vo.getInitialX(), vo.getInitialY(), threadPoolExecutor);
							cellAnim.play();
						}
					}
				}
			}
			
			MagnetRONAnimation.waitAnimationEnd();
			if (aliasType.equals(AliasType.CONSTRACTOR_INVOCATION)) {
				sleepMainThread(500L);
			}
			removeCalledMethodExecutionVertex(srcObjectVertex, methodExec, calledMethodExec);
		} else {
			removeMethodExecutionVertex(srcObjectVertex, methodExec);
		}
	}

	/**
	 * Remove {@code MethodExecutionVertex} on AliasType is {@code AliasType.METHOD_INVOCATION}.
	 * 
	 * @param sourceObjectVertex
	 * @param methodExecution
	 */
	private void removeMethodExecutionVertex(ObjectVertex sourceObjectVertex, MethodExecution methodExecution) {	
		mxgraph.getModel().beginUpdate();
		synchronized (mxgraph.getModel()) {
			try {
				//  Remove source {@code ObjectVertex} from Locals and Arguments of called {@code MethodExecution}'s Vertex.
				if (methodExecToVertexMap.containsKey(methodExecution)) {
					mxCell dstMethodExecVertexCell = (mxCell)methodExecToVertexMap.get(methodExecution).getCell();
					if (!dstMethodExecVertexCell.getParent().equals(getMxDefaultParent())) {
						// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
						dstMethodExecVertexCell.getParent().remove(dstMethodExecVertexCell);
						dstMethodExecVertexCell.setParent(getMxDefaultParent());
					}
					mxgraph.removeCells(new Object[] {dstMethodExecVertexCell});
					objectToVertexMap.get(methodExecution.getThisObjId()).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 calledMethodExecVertex = methodExecToVertexMap.get(calledMethodExecution);

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

								// TODO: Confirm execution order.
								if (!dstMethodExecVertexCell.getParent().equals(getMxDefaultParent())) {
									// If parent of ObjectVertex cell isn't mxDefaltParent, reset parent.
									dstMethodExecVertexCell.getParent().remove(dstMethodExecVertexCell);
									dstMethodExecVertexCell.setParent(getMxDefaultParent());
								}
								mxgraph.removeCells(new Object[] {dstMethodExecVertexCell});
								update();
							} finally {
								mxgraph.getModel().endUpdate();
							}
						} 
					}
				});
				edgeAnim.play();
							
				if (!calledMethodExecution.isStatic()) {
					objectToVertexMap.get(calledMethodExecution.getThisObjId()).getMethodExecutionVertices().remove(methodExecToVertexMap.get(calledMethodExecution));
				} else {
					// 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(POSTPONE_ANIMATION_MILLIS);
		}
	}
	
	/**
	 * 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 srcMethodExecVertexCell = (mxICell)methodExecToVertexMap.get(srcMethodExec).getCell();
				mxICell dstMethodExecVertexCell = (mxICell)methodExecToVertexMap.get(dstMethodExec).getCell();
				Point2D srcMethodExecVertexCellAbsPt = getAbsolutePointforCell(srcMethodExecVertexCell);
				Point2D dstMethodExecVertexCellAbsPt = getAbsolutePointforCell(dstMethodExecVertexCell);

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

	/**
		 * 
		 * @param edge
		 * @param srcCellAbsPt
		 * @param dstCellAbsPt
		 */
		protected void setEdgePoint(mxICell edge, Point2D srcCellAbsPt, Point2D dstCellAbsPt) {
//			mxgraph.orderCells(true, new Object[] {edge});
//			if(srcCellAbsPt.getX() <= dstCellAbsPt.getX()) {
//				// 右下から左上へエッジを引く
//				edge.setStyle("exitX=0.5;exitY=0.5;exitPerimeter=1;entryX=0;entryY=0.5;entryPerimeter=1;");
//			} else {
//				// 左下から右上へエッジを引く
//				edge.setStyle("exitX=0.5;exitY=0.5;exitPerimeter=1;entryX=1;entryY=0.5;entryPerimeter=1;");
//			}
		}

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

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

	/**
	 *  Update the graph on the JFrame by styling the cells and refreshing the mxgraphComponent. 
	 */
	protected void update() {
		/* Given a cell, we can change it's style attributes, for example the color. 
		 * NOTE that you have to call the graphComponent.refresh() function, 
		 * otherwise you won't see the difference! */
		setCellsStyle();
		getGraphComponent().refresh();
	}
	
	private void scrollCellsToVisible(mxICell cell1, mxICell cell2) {
		if (isAutoTracking()) {
			Rectangle rec = new Rectangle();
			Point2D cell1AbsPt = getAbsolutePointforCell(cell1);
			Point2D cell2AbsPt = getAbsolutePointforCell(cell2);
			int cell1X = (int) cell1AbsPt.getX(), cell1Y = (int) cell1AbsPt.getY();
			int cell2X = (int) cell2AbsPt.getX(), cell2Y = (int) cell2AbsPt.getY();
			if (cell1X <= cell2X) {
				if (cell1Y <= cell2Y) {
					cell2X += cell2.getGeometry().getWidth();
					cell2Y += cell2.getGeometry().getHeight();
					rec.setBounds(cell1X, cell1Y, cell2X - cell1X, cell2Y - cell1Y);
				} else {
					cell2X += cell2.getGeometry().getWidth();
					cell1Y += cell1.getGeometry().getHeight();
					rec.setBounds(cell1X, cell2Y, cell2X - cell1X, cell1Y - cell2Y);					
				}
			} else {
				if (cell1Y <= cell2Y) {
					cell1X += cell1.getGeometry().getWidth();
					cell2Y += cell2.getGeometry().getHeight();
					rec.setBounds(cell2X, cell1Y, cell1X - cell2X, cell2Y - cell1Y);
				} else {
					cell1X += cell1.getGeometry().getWidth();
					cell1Y += cell1.getGeometry().getHeight();
					rec.setBounds(cell2X, cell2Y, cell1X - cell2X, cell1Y - cell2Y);					
				}
			}
			scrollRectToVisible(rec);
		}
	}

	private void scrollPointsToVisible(Point2D p1, Point2D p2, boolean center) {
		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);
				} else {
					rec.setBounds(p1X, p2Y, p2X - p1X, p1Y - p2Y);					
				}
			} else {
				if (p1Y <= p2Y) {
					rec.setBounds(p2X, p1Y, p1X - p2X, p2Y - p1Y);
				} else {
					rec.setBounds(p2X, p2Y, p1X - p2X, p1Y - p2Y);					
				}
			}
			
			if (center) {
				int x = (int) (rec.getCenterX() - getWidth() / 2);
				int y = (int) (rec.getCenterY() - getHeight() / 2);
				rec.setBounds(x, y, getWidth(), getHeight());
			}
			scrollRectToVisible(rec);
		}
	}

	@Override
	public void scrollRectToVisible(Rectangle rec) {
		Rectangle visibleRec = getGraphComponent().getGraphControl().getVisibleRect();
		if (isAutoTracking() && !visibleRec.contains(rec)) {
			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 int countChildVertex(ObjectVertex objectVertex) {
		int time = objectVertex.getMethodExecutionVertices().size();
		if(time == 0) {
			return 1;
		}
		for(MethodExecutionVertex mev: objectVertex.getMethodExecutionVertices()) {
			for(ObjectVertex ov: mev.getLocals()) {
				time += countChildVertex(ov);
			}
			for(ObjectVertex ov: mev.getArguments()) {
				return countChildVertex(ov);
			}
		}
		return time;
	}

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

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

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

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

	/**
	 * 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 sourceCell parents contain destinationCell.
	 * 
	 * @param sourceCell
	 * @param destinationCell
	 * @return
	 */
	private boolean isParent(mxICell sourceCell, mxICell destinationCell) {
		mxICell srcParentCell = sourceCell.getParent();
		if (srcParentCell == null || srcParentCell.getValue() == null || destinationCell == null) {
			return false;
		}
		if (srcParentCell.equals(destinationCell)) {
			return true;
		}
		return isParent(srcParentCell, destinationCell);
	}

	protected void reflectCoordinates(DeltaGraphAdapter mxgraph) {
		// TODO Auto-generated method stub
	}
	
	public static Map.Entry<Reference, String> getRelatedInformation(TracePoint relatedPoint, IAliasCollector ac) {
		Statement rpStatement = relatedPoint.getStatement();
		String rpSrcObjId = null;
		String rpDstObjId = null;
		String rpSrcClassName = null;
		String rpDstClassName = null;
		String rpFieldName = null;
	
		// Search for relatedPoint objectReference srcClassName, fieldName.
		if (relatedPoint.isMethodEntry()) {
			// this to another (parameter)
			Alias lastAlias = ac.getAliasList().get(ac.getAliasList().size() - 1);
			if (lastAlias.getAliasType() == Alias.AliasType.FORMAL_PARAMETER) {
				rpSrcObjId = relatedPoint.getMethodExecution().getThisObjId();
				rpDstObjId = lastAlias.getObjectId();
				rpFieldName = "";
				rpSrcClassName = relatedPoint.getMethodExecution().getThisClassName();
				for (ObjectReference r: lastAlias.getMethodExecution().getArguments()) {
					if (r.getId().equals(rpDstObjId)) {
						rpDstClassName =  r.getActualType();
						break;
					}
				}
			}			
		}
		if (rpSrcObjId == null || rpDstObjId == null) {
			if (rpStatement instanceof FieldUpdate) {
				// Format fieldName.
				FieldUpdate rpFieldUpdateStatement = (FieldUpdate) rpStatement;
				rpSrcObjId = rpFieldUpdateStatement.getContainerObjId();
				rpDstObjId = rpFieldUpdateStatement.getValueObjId();
				if (rpFieldUpdateStatement.getFieldName() != null) {
					String rpFieldNames[] = formatFieldName(rpFieldUpdateStatement.getFieldName());
					rpSrcClassName = rpFieldNames[0];
					rpFieldName = rpFieldNames[rpFieldNames.length-1];
				} else {
					rpSrcClassName = rpFieldUpdateStatement.getContainerClassName();
					rpFieldName = "";
				}
				rpDstClassName = rpFieldUpdateStatement.getValueClassName();
			} else if (rpStatement instanceof ArrayUpdate) {
				// container to component
				ArrayUpdate rpArrayUpdateStatement = (ArrayUpdate) rpStatement;
				rpSrcObjId = rpArrayUpdateStatement.getArrayObjectId();
				rpDstObjId = rpArrayUpdateStatement.getValueObjectId();
				rpSrcClassName = rpArrayUpdateStatement.getArrayClassName();
				rpDstClassName = rpArrayUpdateStatement.getValueClassName();
				rpFieldName = "[" + rpArrayUpdateStatement.getIndex() + "]";
			} else if(rpStatement instanceof MethodInvocation) {
				MethodInvocation rpMethodInvStatement = (MethodInvocation) rpStatement;
				MethodExecution rpCalledMethodExec = rpMethodInvStatement.getCalledMethodExecution();
				String rpMethodSig = rpCalledMethodExec.getSignature();
	
				//ArrayやListのときだけラベルを付ける(確実に分かっているものとき)getSignature->contains("List.get(") || "Map.get(") <ホワイトリスト>
//					if (rpMethodExec.getSignature().contains("List.add(") ||
//							rpMethodExec.getSignature().contains("Map.put(")) {
				if (rpCalledMethodExec.isCollectionType()
						&& (rpMethodSig.contains("add(") 
								|| rpMethodSig.contains("set(") 
								|| rpMethodSig.contains("put(") 
								|| rpMethodSig.contains("push(") 
								|| rpMethodSig.contains("addElement("))) {
					
					rpSrcClassName = rpCalledMethodExec.getThisClassName();
					rpDstClassName = rpCalledMethodExec.getArguments().get(0).getActualType();
					rpSrcObjId = rpCalledMethodExec.getThisObjId();
					rpDstObjId = rpCalledMethodExec.getArguments().get(0).getId();
				} else {
					// this to another
					rpSrcClassName = rpMethodInvStatement.getThisClassName();
					rpDstClassName = rpCalledMethodExec.getReturnValue().getActualType();
					rpSrcObjId = rpMethodInvStatement.getThisObjId();
					rpDstObjId = rpCalledMethodExec.getReturnValue().getId();
				}
			}
		}
		return new AbstractMap.SimpleEntry<>(new Reference(rpSrcObjId, rpDstObjId, rpSrcClassName, rpDstClassName), rpFieldName);		
	}
	
	private boolean isAutoTracking() {
		return this.autoTracking;
	}
	
	public void setAutoTracking(boolean autoTracking) {
		if (autoTracking != isAutoTracking()) {
			this.autoTracking = autoTracking;
		}
	}

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