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.Point;
import java.awt.geom.Path2D;
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 javax.swing.JPanel;

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

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

abstract public class MagnetRONViewer extends JPanel {

	protected static Dimension DEFAULT_SIZE = new Dimension(1300, 700);
	protected static String WINDOW_TITLE = "Delta Viewer";

	protected static Dimension VERTEX_OBJECT_SIZE = new Dimension(70, 70);
	protected static Dimension VERTEX_METHOD_EXECUTION_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 DeltaGraphAdapter mxgraph;
	protected mxICell mxDefaultParent;
	protected mxGraphComponent mxgraphComponent;

	protected DeltaAnimation deltaAnimation;
	protected int curFrame = 0;

	public MagnetRONViewer() {
		mxgraph = new DeltaGraphAdapter(new DirectedWeightedPseudograph(DefaultEdge.class));
		mxDefaultParent = (mxCell)mxgraph.getDefaultParent();
		mxgraphComponent = new mxGraphComponent(mxgraph) {
			public mxInteractiveCanvas createCanvas() {
				return new CurvedCanvas(this);
			}
		};
		deltaAnimation = new DeltaAnimation(mxgraph, mxgraphComponent);
		mxgraphComponent.setPreferredSize(DEFAULT_SIZE);
		setLayout(new BorderLayout());
		add(mxgraphComponent, BorderLayout.CENTER);
	}
	
	public mxGraphComponent getGraphComponent() {
		return mxgraphComponent;
	}
	
	public void clear() {
		objectToVertexMap.clear();
		methodExecToVertexMap.clear();
		edgeMap.clear();
		mxgraph.removeCells(mxgraph.getSelectionModel().getCells(), false);
	}

	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);
			System.out.println("\r\n" + i + ": " + alias.getAliasType().toString());
			System.out.println(alias.getObjectId() + ", " + alias.getMethodSignature() + " l." + alias.getLineNo());
			switch(alias.getAliasType()) {
			case RETURN_VALUE:
				moveObjectVertex(alias);
				update();
				break;
			case METHOD_INVOCATION:
				removeMethodExecutionVertex(alias);
				moveObjectVertex(alias);
				update();
				break;
			case CONSTRACTOR_INVOCATION:
				createObjectVertexOnConstractor(alias);
				createMethodExecutionVertex(alias.getObjectId(), ((MethodInvocation)alias.getOccurrencePoint().getStatement()).getCallerSideMethodName(), ((MethodInvocation)alias.getOccurrencePoint().getStatement()).getCalledMethodExecution());
				update();
				removeMethodExecutionVertex(alias);
				update();
				break;
			case FORMAL_PARAMETER:
				moveObjectVertex(alias);
				update();
				break;
			case ACTUAL_ARGUMENT:
				moveObjectVertex(alias);
				update();
				break;
			case THIS:
				if (curFrame == 0 || alias.getObjectId().matches("0")) {
					createMethodExecutionVertex(alias);
					update();
				}
				break;
			case RECEIVER:
				// Make VertexMethodExecution of called method execution.
				MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution();
				if (!methodExecToVertexMap.containsKey(calledMethodExec)) {
					MethodExecution methodExec = alias.getMethodExecution();
					if (!methodExecToVertexMap.containsKey(methodExec)
							&& methodExec.getSignature() != calledMethodExec.getSignature() 
							&& objectToVertexMap.containsKey(methodExec.getThisObjId())) {
						createMethodExecutionVertex(methodExec.getThisObjId(), methodExec.getSignature(), methodExec);
					}
					createMethodExecutionVertex(alias.getObjectId(), calledMethodExec.getSignature(), calledMethodExec);
					update();
				}
				break;
			default:
				break;
			}
			curFrame = i + 1;
			outputLog();
		}
	}

	/**
	 * Create ObjectVertex when CONSTRACTOR_INVOCATION.
	 * @param alias
	 */
	protected void createObjectVertexOnConstractor(Alias alias) {
		ObjectVertex objectVertex = objectToVertexMap.get(alias.getObjectId());
		String sourceObjId = alias.getMethodExecution().getThisObjId();
		mxICell sourceCell = (mxICell)objectToVertexMap.get(sourceObjId).getCell();
		double sourceWidth = sourceCell.getGeometry().getWidth();
		double sourceHeight = sourceCell.getGeometry().getHeight();
		double overlapWidth = sourceWidth * Math.sqrt(2) * 0.1;
		double overlapHeight = sourceHeight  - (sourceHeight * Math.sqrt(2) * 0.1);
		MethodInvocation methodInvocation;
		String fieldName = null;
		if (!alias.getMethodExecution().isCollectionType() && alias.getOccurrencePoint().getStatement() != null) {
			methodInvocation = (MethodInvocation)alias.getOccurrencePoint().getStatement();
			fieldName = methodInvocation.getCallerSideMethodName();
		}
		Point absPtSrcCell = getAbsolutePointforCell(sourceCell);
		mxgraph.getModel().beginUpdate();
		try {
			Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, objectVertex.getLabel(), objectVertex.getLabel(), absPtSrcCell.getX() + overlapWidth, absPtSrcCell.getY() + overlapHeight, VERTEX_OBJECT_SIZE.getWidth(), VERTEX_OBJECT_SIZE.getHeight(), "fillColor=white"); //creates a white vertex. 		
			objectVertex.setCell(vertex);
			Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, fieldName, null, sourceCell, vertex);
			setEdgePoint((mxCell)edge, absPtSrcCell, objectVertex.getInitialPoint());
			edgeMap.put(alias.getMethodExecution().getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
			setCellsStyle();
			deltaAnimation.setVertexAnimation((mxICell)vertex, new mxPoint(objectVertex.getInitialX(), objectVertex.getInitialY()));
			deltaAnimation.startVertexAnimation();
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}
	
	/**
	 * Create ObjectVertex when CONSTRACTOR_INVOCATION is preceded by FORMAL_PARAMETER.
	 * @param alias
	 */
	protected void createObjectVertexOnConstractorByFormalParameter(Alias alias) {
		ObjectVertex objectVertex = objectToVertexMap.get(alias.getObjectId()); // Create cell of this object vertex.
		MethodExecution methodExec = alias.getMethodExecution();
		String srcObjId = methodExec.getArguments().get(0).getId();
		mxICell srcCell = (mxICell)objectToVertexMap.get(srcObjId).getCell();
		double srcWidth = srcCell.getGeometry().getWidth();
		double srcHeight = srcCell.getGeometry().getHeight();
		double overlapWidth = srcWidth * Math.sqrt(2) * 0.1;
		double overlapHeight = srcHeight  - (srcHeight * Math.sqrt(2) * 0.1);
		Point absPtSrcCell = getAbsolutePointforCell(srcCell);
		MethodInvocation methodInv;
		String fieldName = null;
		if (!methodExec.isCollectionType() && alias.getOccurrencePoint().getStatement() != null) {
			methodInv = (MethodInvocation)alias.getOccurrencePoint().getStatement();
			fieldName = methodInv.getCallerSideMethodName();
		}
		mxgraph.getModel().beginUpdate();
		try {
			Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, objectVertex.getLabel(), objectVertex.getLabel(), absPtSrcCell.getX() + overlapWidth, absPtSrcCell.getY() + overlapHeight, VERTEX_OBJECT_SIZE.getWidth(), VERTEX_OBJECT_SIZE.getHeight(), "fillColor=white"); //creates a white vertex. 		
			objectVertex.setCell(vertex);
			Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, fieldName, null, srcCell, vertex);
			setEdgePoint((mxCell)edge, absPtSrcCell, objectVertex.getInitialPoint());
			edgeMap.put(methodExec.getThisClassName() + "." + fieldName, new Edge(fieldName, TypeName.Create, edge));
			setCellsStyle();
			deltaAnimation.setVertexAnimation((mxICell)vertex, new mxPoint(objectVertex.getInitialX(), objectVertex.getInitialY()));
			deltaAnimation.startVertexAnimation();
		} finally {
			mxgraph.getModel().endUpdate();
		}		
	}

	protected void createObjectRefrence(FieldUpdate fieldUpdateStatement, String fieldName) {
		String sourceObjectId = fieldUpdateStatement.getContainerObjId();
		String targetObjectId = fieldUpdateStatement.getValueObjId();
		mxICell sourceCell = (mxICell)objectToVertexMap.get(sourceObjectId).getCell();
		mxICell targetCell = (mxICell)objectToVertexMap.get(targetObjectId).getCell();
		Point absolutePointTargetCell = getAbsolutePointforCell(targetCell);

		targetCell.getParent().remove(targetCell);
		targetCell.setParent(mxDefaultParent);
		targetCell.getGeometry().setX(absolutePointTargetCell.getX());
		targetCell.getGeometry().setY(absolutePointTargetCell.getY());
		Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, fieldUpdateStatement.getFieldName(), fieldName, objectToVertexMap.get(sourceObjectId).getCell(), objectToVertexMap.get(targetObjectId).getCell());
		((mxCell)edge).setStyle("exitX=0;exitY=0.5;exitPerimeter=1;entryX=1;entryY=0.5;entryPerimeter=1;");
		edgeMap.put(fieldUpdateStatement.getFieldName(), new Edge(fieldName, TypeName.Reference, edge));
		//					System.out.println("last" + objectToVertexMap.get(targetObjectId).getInitialX() + ", " + objectToVertexMap.get(targetObjectId).getInitialY());
		deltaAnimation.setVertexAnimation(targetCell, new mxPoint(objectToVertexMap.get(targetObjectId).getInitialX(), objectToVertexMap.get(targetObjectId).getInitialY()));
		deltaAnimation.startVertexAnimation();
		targetCell.getGeometry().setX(objectToVertexMap.get(targetObjectId).getInitialX());
		targetCell.getGeometry().setY(objectToVertexMap.get(targetObjectId).getInitialY());
	}

	protected void createObjectRefrence(String srcClassName, String srcObjId, String tgtObjId) {
		mxICell targetCell;
//		if (objectToVertexMap.containsKey(targetObjId)) {
		targetCell = (mxICell)objectToVertexMap.get(tgtObjId).getCell();
//		} else {
//			targetObjId = methodExec.getCallerMethodExecution().getArguments().get(0).getId();
//			targetCell = (mxICell)objectToVertexMap.get(targetObjId).getCell();			
//		}
		Point absolutePointTargetCell = getAbsolutePointforCell(targetCell);

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.   	
		mxgraph.getModel().beginUpdate();
		try {
			targetCell.getParent().remove(targetCell);
			targetCell.setParent(mxDefaultParent);
			targetCell.getGeometry().setX(absolutePointTargetCell.getX());
			targetCell.getGeometry().setY(absolutePointTargetCell.getY());
			Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, tgtObjId, null, objectToVertexMap.get(srcObjId).getCell(), objectToVertexMap.get(tgtObjId).getCell());
			((mxCell)edge).setStyle("exitX=0;exitY=0.5;exitPerimeter=1;entryX=1;entryY=0.5;entryPerimeter=1;");
			edgeMap.put(tgtObjId, new Edge(null, TypeName.Reference, edge));
//			System.out.println("last" + objectToVertexMap.get(targetObjectId).getInitialX() + ", " + objectToVertexMap.get(targetObjectId).getInitialY());
			deltaAnimation.setVertexAnimation(targetCell, new mxPoint(objectToVertexMap.get(tgtObjId).getInitialX(), objectToVertexMap.get(tgtObjId).getInitialY()));
			deltaAnimation.startVertexAnimation();
			targetCell.getGeometry().setX(objectToVertexMap.get(tgtObjId).getInitialX());
			targetCell.getGeometry().setY(objectToVertexMap.get(tgtObjId).getInitialY());
		} finally {
			mxgraph.getModel().endUpdate();
		}

		System.out.println("rTHIS " + srcClassName + ", " + tgtObjId);
	}

	/**
	 * SourceVertex move targetVertex.
	 * 
	 * @param alias
	 */
	protected void moveObjectVertex(Alias alias) {
		// sourceVertex
		ObjectVertex sourceObjectVertex = objectToVertexMap.get(alias.getObjectId());
		if (alias.getMethodExecution().isStatic() && !methodExecToVertexMap.containsKey(alias.getMethodExecution())) {
			createMethodExecutionVertex(alias.getObjectId(), alias.getMethodExecution().getSignature(), alias.getMethodExecution());
		}
		// targetVertex
		MethodExecutionVertex targetMethodExecVertex = methodExecToVertexMap.get(alias.getMethodExecution());
		System.out.println("moveObjectVertex: " + targetMethodExecVertex);
		System.out.println("moveObjectVertex: " + alias.getMethodExecution().isStatic());
		moveObjectVertex(alias, sourceObjectVertex, targetMethodExecVertex);
		updateObjectVertices();
	}

	/**
	 * Parent : Source VertexObject move target VertexMethodExecution.
	 * 
	 * @param alias
	 * @param sourceVertexObject Source VertexObject.
	 * @param targetVertexMethodExec Target VertexMethodExecution.
	 */
	private void moveObjectVertex(Alias alias, ObjectVertex sourceVertexObject, MethodExecutionVertex targetVertexMethodExec) {
		MethodExecution methodExec = alias.getMethodExecution();
		if (alias.getAliasType().equals(AliasType.RETURN_VALUE) || alias.getAliasType().equals(AliasType.METHOD_INVOCATION)) {
			if (sourceVertexObject.getCell() == null && methodExec.isCollectionType()) {
				if (methodExec.getArguments().isEmpty()) {
					createObjectVertexOnConstractor(alias);
				} else {
					createObjectVertexOnConstractorByFormalParameter(alias);
				}
			}
			moveLocalObjectVertex(methodExec, sourceVertexObject, targetVertexMethodExec);
		} else if (alias.getAliasType().equals(AliasType.FORMAL_PARAMETER)) {
			moveArgumentObjectVertex(methodExec, sourceVertexObject, targetVertexMethodExec);
		} else if (alias.getAliasType().equals(AliasType.ACTUAL_ARGUMENT)) {
			moveActualArgumentObjectVertex(methodExec, sourceVertexObject, targetVertexMethodExec);			
		}
	}

	/**
	 * Source VertexObject move target VertexMethodExecution to Local position from caller MethodExecution.
	 * 
	 * @param callerMethodExec Caller MethodExecution.
	 * @param sourceVertexObject
	 * @param targetVertexMethodExec
	 */
	private void moveLocalObjectVertex(MethodExecution callerMethodExec, ObjectVertex sourceVertexObject, MethodExecutionVertex targetVertexMethodExec) {
		mxICell sourceCell = (mxICell)sourceVertexObject.getCell();
		mxICell targetCell = (mxICell) targetVertexMethodExec.getCell();

		if (sourceCell == targetCell.getParent()) {
			System.out.println("nothing.");
			return;
		}

		//  Remove sourceVertex from Locals and Arguments Vertex of MethodExecution's Vertex.  
		if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getLocals().contains(sourceVertexObject)) {
			methodExecToVertexMap.get(callerMethodExec).getLocals().remove(sourceVertexObject);
			System.out.println(methodExecToVertexMap.get(callerMethodExec).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel());
		}

		if (methodExecToVertexMap.containsKey(callerMethodExec) && methodExecToVertexMap.get(callerMethodExec).getArguments().contains(sourceVertexObject)) {
			methodExecToVertexMap.get(callerMethodExec).getArguments().remove(sourceVertexObject);
			System.out.println(methodExecToVertexMap.get(callerMethodExec).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel());
		}

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

			//			System.out.println(time);

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

			sourceCell.setParent(targetCell.getParent());
			targetCell.getParent().insert(sourceCell);
			System.out.println("moveLocalObjectVertex: " + sourceCell.getId() + " (" + sourceCell.hashCode() + ")" + ", " + sourceCell.getParent().getId() + " (" + sourceCell.getParent().hashCode() + ")");
			System.out.println("                       " + targetCell.getId() + " (" + targetCell.hashCode() + ")" + ", " + targetCell.getParent().getId() + " (" + targetCell.getParent().hashCode() + ")");

			Point absolutePointTargetCell = getAbsolutePointforCell(sourceCell.getParent());
			sourceCell.getGeometry().setX(sourceX - absolutePointTargetCell.getX());
			sourceCell.getGeometry().setY(sourceY - absolutePointTargetCell.getY());

			double sourceWidth = sourceCell.getGeometry().getWidth();
			double sourceHeight = sourceCell.getGeometry().getHeight();
			double targetWidth = targetCell.getGeometry().getWidth();
			double targetHeight = targetCell.getGeometry().getHeight();
			double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1);
			double overlapHeight = sourceHeight  - (sourceHeight * Math.sqrt(2) * 0.1);

//			deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth, targetCell.getGeometry().getY()  - overlapHeight + (sourceHeight * time)));
//			deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth + (sourceWidth * time), targetCell.getGeometry().getY()  - overlapHeight + sourceHeight));
			deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - (sourceWidth / Math.sqrt(2.5)) + (sourceWidth * time), targetCell.getGeometry().getY() + targetHeight));
			deltaAnimation.startVertexAnimation();

			sourceCell.setParent(targetCell.getParent());
			targetCell.getParent().insert(sourceCell);
//			sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth);
//			sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time));
//			sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth + (sourceWidth * time));
//			sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + sourceHeight);
			sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - (sourceWidth / Math.sqrt(2.5)) + (sourceWidth * time));
			sourceCell.getGeometry().setY(targetCell.getGeometry().getY() + targetHeight);
			targetVertexMethodExec.getLocals().add(sourceVertexObject);
			System.out.println("moveLocalObjectVertex: " + targetVertexMethodExec.getLabel() + " :Local: " + sourceVertexObject.getLabel());
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}

	/**
	 * Source VertexObject move target VertexMethodExecution to Argument position from MethodExecution.
	 * 
	 * @param methodExec MethodExecution.
	 * @param sourceVertexObject move
	 * @param targetVertexMethodExec
	 */
	private void moveArgumentObjectVertex(MethodExecution methodExec, ObjectVertex sourceVertexObject, MethodExecutionVertex targetVertexMethodExec) {
		mxICell sourceCell = (mxICell)sourceVertexObject.getCell();
		mxICell targetCell = (mxICell) targetVertexMethodExec.getCell();
		//		mxICell parentTargetMethodExecCell = targetMethodExecCell.getParent();

		//  Remove sourceVertex from Locals and Arguments Vertex of MethodExecution's Vertex.  
		MethodExecution callerMethodExecution = methodExec.getCallerMethodExecution();
		if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getLocals().contains(sourceVertexObject)) {
			methodExecToVertexMap.get(callerMethodExecution).getLocals().remove(sourceVertexObject);
			System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel());
		}

		if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getArguments().contains(sourceVertexObject)) {
			methodExecToVertexMap.get(callerMethodExecution).getArguments().remove(sourceVertexObject);
			System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel());
		}

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

			//			System.out.println(time);

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

			if (!isParent(targetCell, sourceCell)) {
				sourceCell.setParent(targetCell.getParent());
				targetCell.getParent().insert(sourceCell);
				Point absolutePointSourceParentCell = getAbsolutePointforCell(sourceCell.getParent());
				sourceCell.getGeometry().setX(sourceX - absolutePointSourceParentCell.getX());
				sourceCell.getGeometry().setY(sourceY - absolutePointSourceParentCell.getY());

				double sourceWidth = sourceCell.getGeometry().getWidth();
				double sourceHeight = sourceCell.getGeometry().getHeight();
				double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1);
				double overlapHeight = sourceHeight - (sourceHeight * Math.sqrt(2) * 0.1);

				deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth, targetCell.getGeometry().getY()  - overlapHeight + (sourceHeight * time)));
				deltaAnimation.startVertexAnimation();

				//			sourceCell.setParent(targetCell.getParent());
				//			targetCell.getParent().insert(sourceCell);
				sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth);
				sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time));
				targetVertexMethodExec.getArguments().add(sourceVertexObject);			
				System.out.println("moveArgumentObejctVertex" + targetVertexMethodExec.getLabel() + " :Argument: " + sourceVertexObject.getLabel());				
			} else { // 仕様上のバグ, ループが発生
				outputLog();
//				try {
				// ObjectVertex(sourceCell)のクローン生成
//					mxICell cloneSourceCell = (mxICell) mxgraph.addCell(sourceCell.clone());
//
//					cloneSourceCell.setStyle("fillColor=#ffffff;opacity=50;shape=ellipse");
//					cloneSourceCell.setId("clone" + cloneSourceCell.getId());
//					cloneSourceCell.setValue(null);
//					cloneSourceCell.setParent(sourceCell);
//					sourceCell.insert(cloneSourceCell);
//					cloneSourceCell.getGeometry().setX(0);
//					cloneSourceCell.getGeometry().setX(0);
//					cloneSourceCell.getGeometry().setY(0);

				// 元のObjectVertex
				Point absPtSourceCell = getAbsolutePointforCell(sourceCell);
				Point absPtTargetParentCell = getAbsolutePointforCell(targetCell.getParent());

				sourceCell.remove(targetCell.getParent());
				targetCell.getParent().setParent(mxDefaultParent);
				sourceCell.setParent(targetCell.getParent());
				targetCell.getParent().insert(sourceCell);

				targetCell.getParent().getGeometry().setX(absPtTargetParentCell.getX());
				targetCell.getParent().getGeometry().setY(absPtTargetParentCell.getY());
				sourceCell.getGeometry().setX(absPtSourceCell.getX() - absPtTargetParentCell.getX());
				sourceCell.getGeometry().setY(absPtSourceCell.getY() - absPtTargetParentCell.getY());
				sourceCell.setStyle("opacity=50;shape=ellipse");

				double sourceWidth = sourceCell.getGeometry().getWidth();
				double sourceHeight = sourceCell.getGeometry().getHeight();
//					double overlapWidth = targetCell.getGeometry().getWidth() / 2;
				double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1);
				double overlapHeight = sourceHeight - (sourceHeight * Math.sqrt(2) * 0.1);

//					deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() + overlapWidth, targetCell.getGeometry().getY()  - overlapHeight + (sourceHeight * time)));
				deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth + (sourceWidth * time), targetCell.getGeometry().getY()  - overlapHeight + (sourceHeight * time)));
				deltaAnimation.startVertexAnimation();
				outputLog();
//					sourceCell.getGeometry().setX(targetCell.getGeometry().getX() + overlapWidth);
				sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth + (sourceWidth * time));
				sourceCell.getGeometry().setY(targetCell.getGeometry().getY()  - overlapHeight + (sourceHeight * time));

				targetVertexMethodExec.getArguments().add(sourceVertexObject);			

//				} catch (CloneNotSupportedException e) {
//					e.printStackTrace();
//				}
			}
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}

	/**
	 * Source VertexObject move target VertexMethodExecution to Argument position from MethodExecution.
	 * 
	 * @param methodExec MethodExecution.
	 * @param sourceVertexObject
	 * @param targetVertexMethodExec
	 */
	private void moveActualArgumentObjectVertex(MethodExecution methodExec, ObjectVertex sourceVertexObject, MethodExecutionVertex targetVertexMethodExec) {
		mxICell sourceCell = (mxICell)sourceVertexObject.getCell();
		mxICell targetCell = (mxICell) targetVertexMethodExec.getCell();
		//		mxICell parentTargetMethodExecCell = targetMethodExecCell.getParent();

		if (sourceCell == targetCell.getParent()) {
			System.out.println("nothing.");
			return;
		}

		//  Remove sourceVertex from Locals and Arguments Vertex of MethodExecution's Vertex.  
		//		MethodExecution callerMethodExecution = methodExec.getCallerMethodExecution();
		System.out.println(methodExec.getSignature());
		System.out.println(sourceVertexObject.getLabel());
		//		if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getLocals().contains(sourceVertexObject)) {
		//			methodExecToVertexMap.get(callerMethodExecution).getLocals().remove(sourceVertexObject);
		//			System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel());
		//		}
		//
		//		if (methodExecToVertexMap.containsKey(callerMethodExecution) && methodExecToVertexMap.get(callerMethodExecution).getArguments().contains(sourceVertexObject)) {
		//			methodExecToVertexMap.get(callerMethodExecution).getArguments().remove(sourceVertexObject);
		//			System.out.println(methodExecToVertexMap.get(callerMethodExecution).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel());
		//		}
		if (methodExecToVertexMap.containsKey(methodExec) && methodExecToVertexMap.get(methodExec).getLocals().contains(sourceVertexObject)) {
			methodExecToVertexMap.get(methodExec).getLocals().remove(sourceVertexObject);
			System.out.println(methodExecToVertexMap.get(methodExec).getLabel() + " :removeLocal: " + sourceVertexObject.getLabel());
		}

		if (methodExecToVertexMap.containsKey(methodExec) && methodExecToVertexMap.get(methodExec).getArguments().contains(sourceVertexObject)) {
			methodExecToVertexMap.get(methodExec).getArguments().remove(sourceVertexObject);
			System.out.println(methodExecToVertexMap.get(methodExec).getLabel() + " :removeArgument: " + sourceVertexObject.getLabel());
		}

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

			System.out.println(time + ", " + targetVertexMethodExec.getLocals().size());
			//			if (time == 0) time = 1;

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

			sourceCell.setParent(targetCell.getParent());
			targetCell.getParent().insert(sourceCell);
			Point absolutePointSourceParentCell = getAbsolutePointforCell(sourceCell.getParent());
			sourceCell.getGeometry().setX(sourceX - absolutePointSourceParentCell.getX());
			sourceCell.getGeometry().setY(sourceY - absolutePointSourceParentCell.getY());

			double sourceWidth = sourceCell.getGeometry().getWidth();
			double sourceHeight = sourceCell.getGeometry().getHeight();
			double targetWidth = targetCell.getGeometry().getWidth();
			double targetHeight = targetCell.getGeometry().getHeight();
			double overlapWidth = sourceWidth - (sourceWidth * Math.sqrt(2) * 0.1);
			double overlapHeight = sourceHeight - (sourceHeight * Math.sqrt(2) * 0.1);

//			deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - overlapWidth, targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time)));
			deltaAnimation.setVertexAnimation(sourceCell, new mxPoint(targetCell.getGeometry().getX() - (sourceWidth / Math.sqrt(3)) + (sourceWidth * time), targetCell.getGeometry().getY() + targetHeight));
			deltaAnimation.startVertexAnimation();
//			sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - overlapWidth);
//			sourceCell.getGeometry().setY(targetCell.getGeometry().getY() - overlapHeight + (sourceHeight * time));
			sourceCell.getGeometry().setX(targetCell.getGeometry().getX() - (sourceWidth / Math.sqrt(3)) + (sourceWidth * time));
			sourceCell.getGeometry().setY(targetCell.getGeometry().getY() + targetHeight);

			targetVertexMethodExec.getArguments().add(sourceVertexObject);			
			System.out.println("moveActualArgumentObjectVertex: " + targetVertexMethodExec.getLabel() + " :Argument: " + sourceVertexObject.getLabel());
		} finally {
			mxgraph.getModel().endUpdate();
		}
	}

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

	abstract protected void createMethodExecutionVertex(Alias alias);

	/**
	 * Parent : Create MethodExecutionVertex.
	 * 
	 * @param objId
	 * @param methodSignature Called or this MethodSignature.
	 * @param methodExec Called or this MethodExecution.
	 */
	protected void createMethodExecutionVertex(String objId, String methodSignature, MethodExecution methodExec) {
		if (methodSignature == null) methodSignature = methodExec.getSignature();

		if (methodSignature.matches(".+\\(.*\\)")) {
			System.out.println(methodSignature);
			methodSignature = formatMethodSignature(methodSignature, methodExec.getThisClassName());
		}

		// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
		mxgraph.getModel().beginUpdate();
		try {
			if (methodExec.isStatic() && !objId.equals("0")) {
				objId = methodExec.getCallerMethodExecution().getThisObjId();
			}

			Object object = objectToVertexMap.get(objId).getCell();

//			if (object != null) {
			double xCor = VERTEX_OBJECT_SIZE.getWidth() * 0.1;
			double yCor =  VERTEX_OBJECT_SIZE.getHeight() * 0.5;
			double standardX = xCor;
			double standardY = 0;
			int time = objectToVertexMap.get(objId).getVertexMethodExecutions().size();
			System.out.println(time);
			if (time >= 1) {
				mxICell standardCell = (mxICell) objectToVertexMap.get(objId).getVertexMethodExecutions().get(0).getCell();
				standardX = standardCell.getGeometry().getX();
				standardY = standardCell.getGeometry().getY();
				time-=1;
			}

			Object vertex = mxgraph.insertDeltaVertex(object, methodSignature, methodSignature, "fillColor=white"); //creates a white vertex. 
			System.out.println("makeVertexMethodExecution: " + ((mxICell)vertex).getId() + " in " + objId + " (" + standardX + ", " + yCor * (time + 1) + standardY + ")");

			MethodExecutionVertex vertexMethodExecution =  new MethodExecutionVertex(methodSignature, vertex, standardX , yCor * (time + 1) + standardY, VERTEX_METHOD_EXECUTION_SIZE.getWidth(), VERTEX_METHOD_EXECUTION_SIZE.getHeight());
			//			Object vertex = mxgraph.insertVertex(object, methodSignature, methodSignature, 0, 0, 0, 0, "fillColor=white", true); //creates a white vertex.
			//			Object vertex = mxgraph.insertDeltaVertex(mxDefaultParent, methodSignature, methodSignature, "fillColor=white"); //creates a white vertex. 
			//			VertexMethodExecution vertexMethodExecution =  new VertexMethodExecution(methodSignature, vertex, getXForCell(objectId) + (xCor * (time + 1)), getYForCell(objectId) + (yCor * (time + 1)), VERTEX_METHOD_EXECUTION_SIZE.getWidth(), VERTEX_METHOD_EXECUTION_SIZE.getHeight());
			methodExecToVertexMap.put(methodExec, vertexMethodExecution);
			if(methodExecToVertexMap.size() > 1) {
				((mxICell)vertex).setVisible(false);
				createEdgeToMethodExecution();
			}
			objectToVertexMap.get(objId).addMethodExecution(vertexMethodExecution);
//			}
		} finally {
			mxgraph.getModel().endUpdate();
		}
		setCellsStyle();
	}

	/**
	 * Remove VertexMethodExecution on AliasType is MethodInvocation of alias.
	 * 
	 * @param alias
	 */
	private void removeMethodExecutionVertex(Alias alias) {
		// sourceVertex
		ObjectVertex sourceVertexObject = objectToVertexMap.get(alias.getObjectId());
		MethodExecution methodExec = alias.getMethodExecution();

		if(alias.getAliasType().equals(AliasType.METHOD_INVOCATION) || alias.getAliasType().equals(AliasType.CONSTRACTOR_INVOCATION)) {
			MethodExecution calledMethodExec = ((MethodInvocation) alias.getOccurrencePoint().getStatement()).getCalledMethodExecution();
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			try {
				List<ObjectVertex> arguments = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getArguments());
				List<ObjectVertex> locals = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getLocals());
				if (arguments.size() != 0) {
					for (ObjectVertex vo: arguments) {
						if (vo != sourceVertexObject) {
							System.out.println("argumentRemove");
							mxICell cell = (mxICell)vo.getCell();
							Point absolutePointCell = getAbsolutePointforCell(cell);
							cell.getParent().remove(cell);
							cell.setParent(mxDefaultParent);
							cell.getGeometry().setX(absolutePointCell.getX());
							cell.getGeometry().setY(absolutePointCell.getY());
							deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
							deltaAnimation.startVertexAnimation();
							methodExecToVertexMap.get(calledMethodExec).getArguments().remove(vo);
						}
					}
				}
				if (locals.size() != 0) {
					for (ObjectVertex vo: locals) {
						if (vo != sourceVertexObject) {
							System.out.println("localRemove");
							mxICell cell = (mxICell)vo.getCell();
							Point absolutePointCell = getAbsolutePointforCell(cell);
							cell.getParent().remove(cell);
							cell.setParent(mxDefaultParent);
							cell.getGeometry().setX(absolutePointCell.getX());
							cell.getGeometry().setY(absolutePointCell.getY());
							deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
							deltaAnimation.startVertexAnimation();
							methodExecToVertexMap.get(calledMethodExec).getLocals().remove(vo);
						}
					}
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}


			removeCalledMethodExecutionVertex(sourceVertexObject, methodExec, calledMethodExec);
		} else {
			removeMethodExecutionVertex(sourceVertexObject, methodExec);
		}
	}

	/**
	 * Remove VertexMethodExecution on AliasType is MethodInvocation of alias.
	 * 
	 * @param sourceVertexObject
	 * @param methodExec
	 */
	private void removeMethodExecutionVertex(ObjectVertex sourceVertexObject, MethodExecution methodExec) {	
		//  Remove sourceVertex from Locals and Arguments Vertex of CalledMethodExecution's Vertex.  
		if (methodExecToVertexMap.containsKey(methodExec)) {
			mxCell targetVertexCell = (mxCell)methodExecToVertexMap.get(methodExec).getCell();
			targetVertexCell.getParent().remove(targetVertexCell);
			targetVertexCell.setParent(mxDefaultParent);
			mxgraph.removeCells(new Object[] {targetVertexCell});
			objectToVertexMap.get(methodExec.getThisObjId()).getVertexMethodExecutions().remove(methodExecToVertexMap.get(methodExec));
			methodExecToVertexMap.remove(methodExec);
			edgeMap.remove(methodExec.getSignature());
			updateObjectVertices();
		}
	}

	/**
	 * Remove CalledVertexMethodExecution on AliasType is MethodInvocation of alias.
	 * 
	 * @param sourceVertexObject
	 * @param methodExec
	 * @param calledMethodExec
	 */
	protected void removeCalledMethodExecutionVertex(ObjectVertex sourceVertexObject, MethodExecution methodExec, MethodExecution calledMethodExec) {	
		outputLog();

		//  Remove sourceVertex from Locals and Arguments Vertex of CalledMethodExecution's Vertex.  
		if (methodExecToVertexMap.containsKey(calledMethodExec)) {
			mxICell sourceVertexCell = null;
			mxCell targetVertexCell = null;
			
			if (methodExec != null) {
				sourceVertexCell = (mxICell)methodExecToVertexMap.get(methodExec).getCell();
				targetVertexCell = (mxCell)methodExecToVertexMap.get(calledMethodExec).getCell();
			}
//			if(!calledMethodExec.isStatic()) {
			// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology.
			mxgraph.getModel().beginUpdate();
			try {
				// Dangerous
				List<ObjectVertex> arguments = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getArguments());
				List<ObjectVertex> locals = new ArrayList<>(methodExecToVertexMap.get(calledMethodExec).getLocals());
				if (arguments.size() != 0) {
					for (ObjectVertex vo: arguments) {
						if (vo != sourceVertexObject) {
							mxICell cell = (mxICell)vo.getCell();
							Point absolutePointCell = getAbsolutePointforCell(cell);
							System.out.println(cell);
							System.out.println(vo.getInitialX() + ", " + vo.getInitialY());
							System.out.println(cell.getGeometry().getX() + ", " +  cell.getGeometry().getY());
							System.out.println(absolutePointCell);
							if (cell.getParent() != mxDefaultParent) {
								cell.getParent().remove(cell);
								cell.setParent(mxDefaultParent);
							}
							if (!absolutePointCell.equals(vo.getInitialPoint())) {
								cell.getGeometry().setX(absolutePointCell.getX());
								cell.getGeometry().setY(absolutePointCell.getY());
								deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
								deltaAnimation.startVertexAnimation();
							}
							methodExecToVertexMap.get(calledMethodExec).getArguments().remove(vo);
						}
					}
				}
				if (locals.size() != 0) {
					for (ObjectVertex vo: locals) {
						if (vo != sourceVertexObject) {
							mxICell cell = (mxICell)vo.getCell();
							Point absolutePointCell = getAbsolutePointforCell(cell);
							System.out.println(cell);
							System.out.println(vo.getInitialX() + ", " + vo.getInitialY());
							System.out.println(cell.getGeometry().getX() + ", " +  cell.getGeometry().getY());
							System.out.println(absolutePointCell);
							if (cell.getParent() != mxDefaultParent) {
								cell.getParent().remove(cell);
								cell.setParent(mxDefaultParent);
							}
							if (!absolutePointCell.equals(vo.getInitialPoint())) {
								cell.getGeometry().setX(absolutePointCell.getX());
								cell.getGeometry().setY(absolutePointCell.getY());
								deltaAnimation.setVertexAnimation(cell, new mxPoint(vo.getInitialX(), vo.getInitialY()));
								deltaAnimation.startVertexAnimation();
							}
							methodExecToVertexMap.get(calledMethodExec).getLocals().remove(vo);
						}
					}
				}
				
				if (sourceVertexCell == null || targetVertexCell == null) return;
				
				mxgraph.removeCells(mxgraph.getEdgesBetween(sourceVertexCell, targetVertexCell));
				try {
					mxICell cloneTargetVertexCell = (mxICell) mxgraph.addCell(targetVertexCell.clone());
					Point absolutPointSourceVertexCell = getAbsolutePointforCell(sourceVertexCell);
					Point absolutPointTargetVertexCell = getAbsolutePointforCell(targetVertexCell);
					cloneTargetVertexCell.getGeometry().setX(absolutPointTargetVertexCell.getX());
					cloneTargetVertexCell.getGeometry().setY(absolutPointTargetVertexCell.getY());
					cloneTargetVertexCell.setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
					cloneTargetVertexCell.setValue(null);
					Object tempEdge = mxgraph.insertEdge(mxDefaultParent, null, null, sourceVertexCell, cloneTargetVertexCell);
					((mxCell)tempEdge).setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
					deltaAnimation.setReductionEdgeAnimation(cloneTargetVertexCell, new mxPoint(absolutPointSourceVertexCell.getX(), absolutPointSourceVertexCell.getY() + sourceVertexCell.getGeometry().getHeight()));
					deltaAnimation.startReductionEdgeAnimation();
//						deltaAnimation.setReductionEdgeAnimation(new mxPoint(absolutPointSourceVertexCell.getX() + (sourceVertexCell.getGeometry().getWidth() / 2), absolutPointSourceVertexCell.getY() + sourceVertexCell.getGeometry().getHeight()), new mxPoint(absolutPointTargetVertexCell.getX() + (targetVertexCell.getGeometry().getWidth() / 2), absolutPointTargetVertexCell.getY()));
//						deltaAnimation.startReductionEdgeAnimation();
					mxgraph.removeCells(new Object[]{cloneTargetVertexCell});
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
			} finally {
				mxgraph.getModel().endUpdate();
			}
			((mxCell)targetVertexCell.getParent()).remove(targetVertexCell);
			targetVertexCell.setParent(mxDefaultParent);
			mxgraph.removeCells(new Object[] {targetVertexCell});
			if (!calledMethodExec.isStatic()) {
				objectToVertexMap.get(calledMethodExec.getThisObjId()).getVertexMethodExecutions().remove(methodExecToVertexMap.get(calledMethodExec));
			} else {
				objectToVertexMap.get(calledMethodExec.getCallerMethodExecution().getThisObjId()).getVertexMethodExecutions().remove(methodExecToVertexMap.get(calledMethodExec));
			}
			methodExecToVertexMap.get(calledMethodExec).getLocals().remove(sourceVertexObject);
			methodExecToVertexMap.remove(calledMethodExec);
			edgeMap.remove(methodExec.getSignature());
//				moveInitialVertexObject(methodExec);
//				updateObjectVerticesSize();
//			} else {
//				((mxCell)targetVertexCell.getParent()).remove(targetVertexCell);
//				targetVertexCell.setParent(mxDefaultParent);
//				mxgraph.removeCells(new Object[] {targetVertexCell});
//				methodExecToVertexMap.get(calledMethodExec).getLocals().remove(sourceVertexObject);
//				methodExecToVertexMap.remove(calledMethodExec);
//			}
		}
		outputLog();
	}

	/** Make EdgeMethodExecution. */
	private void createEdgeToMethodExecution() {
		List<MethodExecution> methodExecList = new ArrayList<>(methodExecToVertexMap.keySet());

		// BUG : Edge Orientation Reverse.
		for (int i = 0; i < methodExecList.size() - 1; i++) {
			MethodExecution sourceMethodExec = methodExecList.get(i);
			MethodExecution targetMethodExec = methodExecList.get(i + 1);
			String methodSignature = sourceMethodExec.getSignature();
			if (!edgeMap.containsKey(methodSignature)) {
				mxICell sourceVertexCell = (mxICell)methodExecToVertexMap.get(sourceMethodExec).getCell();
				mxICell targetVertexCell = (mxICell)methodExecToVertexMap.get(targetMethodExec).getCell();
//				if (!targetMethodExec.isStatic()) {
				Point absolutPointSourceVertexCell = getAbsolutePointforCell(sourceVertexCell);
				Point absolutPointTargetVertexCell = getAbsolutePointforCell(targetVertexCell);
				// Add a vertex to the graph in a transactional fashion. The vertex is actually a 'cell' in jgraphx terminology    	
				mxgraph.getModel().beginUpdate();
				try {
					try {
						mxICell cloneTargetVertexCell = (mxICell) mxgraph.addCell(targetVertexCell.clone());
						cloneTargetVertexCell.getGeometry().setX(absolutPointSourceVertexCell.getX());
						cloneTargetVertexCell.getGeometry().setY(absolutPointSourceVertexCell.getY() + targetVertexCell.getGeometry().getHeight());
						cloneTargetVertexCell.setStyle("fillColor=none;strokeColor=none;fontColor=#008000;");
						cloneTargetVertexCell.setValue(null);
						cloneTargetVertexCell.setVisible(true);
						Object tempEdge = mxgraph.insertEdge(mxDefaultParent, null, null, sourceVertexCell, cloneTargetVertexCell);
						((mxCell)tempEdge).setStyle("dashed=1;strokeColor=#008000;exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;endArrow=none");
						deltaAnimation.setExpandEdgeAnimation(cloneTargetVertexCell, new mxPoint(absolutPointTargetVertexCell.getX(), absolutPointTargetVertexCell.getY()));
						deltaAnimation.startExpandEdgeAnimation();
						targetVertexCell.setVisible(true);
//						deltaAnimation.setExpandEdgeAnimation(new mxPoint(absolutPointSourceVertexCell.getX() + (sourceVertexCell.getGeometry().getWidth() / 2), absolutPointSourceVertexCell.getY() + sourceVertexCell.getGeometry().getHeight()), new mxPoint(absolutPointTargetVertexCell.getX() + (targetVertexCell.getGeometry().getWidth() / 2), absolutPointTargetVertexCell.getY()));
//						deltaAnimation.startExpandEdgeAnimation();
						Object edge = mxgraph.insertDeltaEdge(mxDefaultParent, methodSignature, null, sourceVertexCell, targetVertexCell);
						((mxCell)edge).getParent().remove(((mxCell)edge));
						((mxCell)edge).setParent(mxDefaultParent);
						mxgraph.orderCells(false, new Object[] {edge});
						((mxCell)edge).setStyle("exitX=0.5;exitY=1;exitPerimeter=1;entryX=0.5;entryY=0;entryPerimeter=1;");
						mxgraph.removeCells(new Object[]{cloneTargetVertexCell});
						edgeMap.put(methodSignature, new Edge(methodSignature, TypeName.Call, edge));
					} catch (CloneNotSupportedException e) {
						e.printStackTrace();
					}
				} finally {
					mxgraph.getModel().endUpdate();
				}
//				} else {
//					targetVertexCell.setVisible(true);
//				}

			}
		}
	}

	protected void setEdgePoint(mxICell edge, Point absPtSrcCell, Point absPtTgtCell) {
			if(absPtSrcCell.getX() < absPtTgtCell.getX()) {
	//							if(isSrcSideChanged) {
				// 右下から左上へエッジを引く
				edge.setStyle("exitX=1;exitY=1;exitPerimeter=1;entryX=0;entryY=0;entryPerimeter=1;");
			} else {
				// 左下から右上へエッジを引く
				edge.setStyle("exitX=0;exitY=1;exitPerimeter=1;entryX=1;entryY=0;entryPerimeter=1;");
			}
		}

	/** Set style of All cells. */
	protected void setCellsStyle() {
		List<Object> vertexObject = new ArrayList<>();
		List<Object> staticVertexObject = new ArrayList<>();
		List<Object> alignMiddleVertex = new ArrayList<>();
		List<Object> alignTopVertex = new ArrayList<>();
		List<Object> edgeObject = new ArrayList<>();
		List<Object> edgeObjectCreate = new ArrayList<>();
		List<Object> edgeMethodExec = new ArrayList<>();
		List<Object> roundEdge = new ArrayList<>();
	
		for (Entry<String, ObjectVertex> objectToVertexEntry: objectToVertexMap.entrySet()) {
			String key = objectToVertexEntry.getKey();
			ObjectVertex objectVertex = objectToVertexEntry.getValue();
			if (key.matches("0")) {
				staticVertexObject.add(objectVertex.getCell());
			} else {
				vertexObject.add(objectVertex.getCell());
			}
			if(objectVertex.getVertexMethodExecutions().size() == 0) {
				alignMiddleVertex.add(objectVertex.getCell());
			} else {
				alignTopVertex.add(objectVertex.getCell());
			}
		}
	
		List<MethodExecutionVertex> vertexMethodExecList = new ArrayList<>(methodExecToVertexMap.values());
		Collections.reverse(vertexMethodExecList);
		for (int i = 0; i < vertexMethodExecList.size(); i++) {
			switch(i) {
			case 0:
				((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ff7fbf");
				break;
			case 1:
				((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ff99cc");
				break;
			case 2:
				((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ffb2d8");
				break;
			case 3:
				((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ffcce5");
				break;
			case 4:
				((mxICell)vertexMethodExecList.get(i).getCell()).setStyle("fillColor=#ffe0ef");
				break;
			default:
				break;
			}
		}
	
		for (Edge edge: edgeMap.values()) {
			roundEdge.add(edge.getCell());
			switch(edge.getTypeName()) {
			case Reference:
				edgeObject.add(edge.getCell());
				break;
			case Create:
				edgeObject.add(edge.getCell());
				edgeObjectCreate.add(edge.getCell());
				break;
			case Call:
				edgeMethodExec.add(edge.getCell());
				break;
			default:
				break;
			}
		}
	
		/*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! */
		mxgraph.setCellStyles(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE, vertexObject.toArray(new Object[vertexObject.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE, vertexObject.toArray(new Object[vertexObject.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_UNDERLINE, true, vertexObject.toArray(new Object[vertexObject.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE, alignMiddleVertex.toArray(new Object[alignMiddleVertex.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_TOP, alignTopVertex.toArray(new Object[alignTopVertex.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM, edgeObject.toArray(new Object[edgeObject.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, edgeObjectCreate.toArray(new Object[edgeObjectCreate.size()]));
		//		mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ENTITY_RELATION, edgeObject.toArray(new Object[edgeObject.size()]));
		//		mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ORTHOGONAL, edgeObject.toArray(new Object[edgeObject.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()]));
		//		mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.SHAPE_CURVE, edgeObject.toArray(new Object[edgeObject.size()]));
		mxgraph.setCellStyles(mxConstants.STYLE_STROKECOLOR, "#008000", edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
		mxgraph.setCellStyleFlags(mxConstants.STYLE_DASHED, 1, true, edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
		//		mxgraph.setCellStyleFlags(mxConstants.STYLE_AUTOSIZE, 1, true, vertexObject.toArray(new Object[vertexObject.size()]));
		//		mxgraph.setCellStyles(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ORTHOGONAL, edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
		//		mxgraph.setCellStyles(mxConstants.STYLE_ELBOW, mxConstants.ELBOW_VERTICAL, edgeMethodExec.toArray(new Object[edgeMethodExec.size()]));
	}

	private void setFrameSize(int width, int height) {
		DEFAULT_SIZE.setSize(width, height);
	}

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

	/** Update graph on JFrame and set Cell style. */
	protected void update() {
		setCellsStyle();
		mxgraphComponent.refresh();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private int countChildVertex(ObjectVertex vertexObject) {
		int time = vertexObject.getVertexMethodExecutions().size();
		if(time == 0) {
			return 1;
		}
		for(MethodExecutionVertex vertexMethodExecution: vertexObject.getVertexMethodExecutions()) {
			for(ObjectVertex vo: vertexMethodExecution.getLocals()) {
				time += countChildVertex(vo);
			}
			for(ObjectVertex vo: vertexMethodExecution.getArguments()) {
				return countChildVertex(vo);
			}
		}
		System.out.println(vertexObject.getLabel() + ": " + time);
		return time;
	}

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

	protected String formatMethodSignature(String methodSignature, String thisClassName) {
		// TODO Modify algorithm formatMethodSignature().
		// Step1 : split "("
//		String[] methodSignatures = methodSignature.split("\\(");
		methodSignature = methodSignature.substring(0, methodSignature.lastIndexOf('('));
		// Step2 : split " "
		String[] methodSignatures = methodSignature.split(" ");
		String tmpMethodSignature = methodSignatures[methodSignatures.length-1];
		// Step3 : split "."
		String[] thisClassNames = thisClassName.split("\\.");
		methodSignatures = tmpMethodSignature.split("\\.");
		StringBuffer sb = new StringBuffer();
		int i = methodSignatures.length - 2;
		int count = methodSignature.split("\\(").length - 1;
		if (count > 0) i -= count;
		if (i >= 0 && !thisClassNames[thisClassNames.length - 1].equals(methodSignatures[i])) {
			if (thisClassNames[thisClassNames.length - 1].equals(methodSignatures[i + 1])) i += 1;
			sb.append(methodSignatures[i]);
			if (methodSignatures.length - i > 1) sb.append(".");
		}
		for (i = i + 1; i < methodSignatures.length; i++) {
			sb.append(methodSignatures[i]);
			if (methodSignatures.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();
	}

	protected void outputLog() {
		for (Object object: mxgraph.getChildCells(mxDefaultParent)) {
			System.out.println(object + " " + object.hashCode());
			for (int i = 0; i < ((mxICell)object).getChildCount(); i++) {
				System.out.println("   " + ((mxICell)object).getChildAt(i) + " " + object.hashCode());
			}
		}
		System.out.println("\nObject");
		for (Entry<String, ObjectVertex> e: objectToVertexMap.entrySet()) {
			String objId = e.getKey();
			ObjectVertex vo = e.getValue();
			if (vo.getCell() != null) {
				System.out.println(vo.getLabel() + " (" + objId + ")" + " " + vo.getCell().hashCode());				
			} else {
				System.out.println(vo.getLabel() + " (" + objId + ")");								
			}
			for (MethodExecutionVertex vme: vo.getVertexMethodExecutions()) {
				System.out.println("   " + vme.getLabel());
				for (ObjectVertex vmevo: vme.getArguments()) {
					System.out.println("      Argument: " + vmevo.getLabel());					
				}
				for (ObjectVertex vmevo: vme.getLocals()) {
					System.out.println("      Local: " + vmevo.getLabel());					
				}
			}
		}
		System.out.println("\nEdge");
		for (Edge e: edgeMap.values()) {
			System.out.println(e.getLabel() + "(" + ((mxICell)e.getCell()).getId() + ")");
			if (((mxICell)e.getCell()).getParent() != null) {
				System.out.println(" " + ((mxICell)e.getCell()).getParent().getId());
			}
		}
	}

	/**
	 * Whether sourceCell parents contain targetCell.
	 * @param sourceCell
	 * @param targetCell
	 * @return
	 */
	private boolean isParent(mxICell sourceCell, mxICell targetCell) {
		mxICell sourceParentCell = sourceCell.getParent();
		if (sourceParentCell == null || sourceParentCell.getValue() == null) {
			return false;
		}
		if (sourceParentCell == targetCell) {
			return true;
		}
		System.out.println(sourceCell.getId() + ", " + sourceParentCell.getId());
		return isParent(sourceParentCell, targetCell);
	}

	protected void reflectCoordinates(DeltaGraphAdapter mxgraph) {
		// TODO Auto-generated method stub
	}
	
	public static Map.Entry<Reference, String> getRelatedInformation(TracePoint rp) {
		Statement rpStatement = rp.getStatement();
		String rpSrcObjId = null;
		String rpDstObjId = null;
		String rpSrcClassName = null;
		String rpDstClassName = null;
		String rpFieldName = null;
	
		// Search for relatedPoint objectReference srcClassName, fieldName.
		if(rpStatement instanceof FieldUpdate) {
			// Format fieldName.
			FieldUpdate rpFieldUpdateStatement = (FieldUpdate) rpStatement;
			rpSrcObjId = rpFieldUpdateStatement.getContainerObjId();
			rpDstObjId = rpFieldUpdateStatement.getValueObjId();
			String rpFieldNames[] = formatFieldName(rpFieldUpdateStatement.getFieldName());
			rpSrcClassName = rpFieldNames[0];
			rpDstClassName = rpFieldUpdateStatement.getValueClassName();
			rpFieldName = rpFieldNames[rpFieldNames.length-1];
		} else if (rpStatement instanceof ArrayUpdate) {
			// container to component
			ArrayUpdate rpArrayUpdateStatement = (ArrayUpdate) rpStatement;
			rpSrcObjId = rpArrayUpdateStatement.getArrayObjectId();
			rpDstObjId = rpArrayUpdateStatement.getValueObjectId();
			rpSrcClassName = rpArrayUpdateStatement.getArrayClassName();
			rpDstClassName = rpArrayUpdateStatement.getValueClassName();
			rpFieldName = "[" + rpArrayUpdateStatement.getIndex() + "]";
		} else if(rpStatement instanceof MethodInvocation) {
			MethodInvocation rpMethodInvStatement = (MethodInvocation) rpStatement;
			MethodExecution rpCalledMethodExec = rpMethodInvStatement.getCalledMethodExecution();
			String rpMethodSig = rpCalledMethodExec.getSignature();

			//ArrayやListのときだけラベルを付ける(確実に分かっているものとき)getSignature->contains("List.get(") || "Map.get(") <ホワイトリスト>
//				if (rpMethodExec.getSignature().contains("List.add(") ||
//						rpMethodExec.getSignature().contains("Map.put(")) {
			if (rpCalledMethodExec.isCollectionType()
					&& (rpMethodSig.contains("add(") 
							|| rpMethodSig.contains("set(") 
							|| rpMethodSig.contains("put(") 
							|| rpMethodSig.contains("push(") 
							|| rpMethodSig.contains("addElement("))) {
				
				rpSrcClassName = rpCalledMethodExec.getThisClassName();
				rpDstClassName = rpCalledMethodExec.getArguments().get(0).getActualType();
				rpSrcObjId = rpCalledMethodExec.getThisObjId();
				rpDstObjId = rpCalledMethodExec.getArguments().get(0).getId();
			} else {
				// this to another
				rpSrcClassName = rpMethodInvStatement.getThisClassName();
				rpDstClassName = rpCalledMethodExec.getReturnValue().getActualType();
				rpSrcObjId = rpMethodInvStatement.getThisObjId();
				rpDstObjId = rpCalledMethodExec.getReturnValue().getId();
			}
		}
		return new AbstractMap.SimpleEntry<>(new Reference(rpSrcObjId, rpDstObjId, rpSrcClassName, rpDstClassName), rpFieldName);		
	}

	protected class CurvedCanvas extends mxInteractiveCanvas {
		mxIShape shape = new CurvedConnector();

		public CurvedCanvas(mxGraphComponent mxGraphComponent) {
			super(mxGraphComponent);
		}

		public Object drawCell(mxCellState state) {
			if (!(state.getCell() instanceof mxCell) || !((mxCell)state.getCell()).isEdge() || state.getAbsolutePointCount() == 2) {
				return super.drawCell(state);
			}
			Map<String, Object> style = state.getStyle();

			if (g != null) {
				// Creates a temporary graphics instance for drawing this shape
				float opacity = mxUtils.getFloat(style, mxConstants.STYLE_OPACITY, 100);
				Graphics2D previousGraphics = g;
				g = createTemporaryGraphics(style, opacity, state);
				shape.paintShape(this, state);				
				g.dispose();
				g = previousGraphics;
			}

			return shape;
		}
	}

	protected class CurvedConnector extends mxConnectorShape {
		public void paintShape(mxGraphics2DCanvas canvas, mxCellState state) {
			if (state.getAbsolutePointCount() > 1
					&& configureGraphics(canvas, state, false)) {
				List<mxPoint> pts = new ArrayList<mxPoint>(
						state.getAbsolutePoints());
				Map<String, Object> style = state.getStyle();

				// Paints the markers and updates the points
				// Switch off any dash pattern for markers
				boolean dashed = mxUtils.isTrue(style, mxConstants.STYLE_DASHED);
				Object dashedValue = style.get(mxConstants.STYLE_DASHED);

				if (dashed) {
					style.remove(mxConstants.STYLE_DASHED);
					canvas.getGraphics().setStroke(canvas.createStroke(style));
				}

				translatePoint(pts, 0,
						paintMarker(canvas, state, true));
				translatePoint(
						pts,
						pts.size() - 1,
						paintMarker(canvas, state, false));

				if (dashed) {
					// Replace the dash pattern
					style.put(mxConstants.STYLE_DASHED, dashedValue);
					canvas.getGraphics().setStroke(canvas.createStroke(style));
				}

				// Paints the shape and restores the graphics object
				if (state.getAbsolutePointCount() == 4) {
					double sx = state.getAbsolutePoint(0).getX();
					double sy = state.getAbsolutePoint(0).getY();
					double tx1 = state.getAbsolutePoint(1).getX();
					double ty1 = state.getAbsolutePoint(1).getY();
					double tx2 = state.getAbsolutePoint(2).getX();
					double ty2 = state.getAbsolutePoint(2).getY();					
					double ex = state.getAbsolutePoint(3).getX();
					double ey = state.getAbsolutePoint(3).getY();
					Path2D.Double p = new Path2D.Double();
					p.moveTo((int) sx, (int) sy);
					p.quadTo((int) tx2, (int) ty2, (int) ex, (int) ey);
//					p.curveTo((int) tx1, (int) ty1, (int) tx2, (int) ty2, (int) ex, (int) ey);
					canvas.getGraphics().draw(p);
				} else if (state.getAbsolutePointCount() == 3) {
					double sx = state.getAbsolutePoint(0).getX();
					double sy = state.getAbsolutePoint(0).getY();
					double tx = state.getAbsolutePoint(1).getX();
					double ty = state.getAbsolutePoint(1).getY();
					double ex = state.getAbsolutePoint(2).getX();
					double ey = state.getAbsolutePoint(2).getY();
					Path2D.Double p = new Path2D.Double();
					p.moveTo((int) sx, (int) sy);
					p.quadTo((int) tx, (int) ty, (int) ex, (int) ey);
					canvas.getGraphics().draw(p);
				}
			}
		}

		private void translatePoint(List<mxPoint> points, int index, mxPoint offset) {
			if (offset != null) {
				mxPoint pt = (mxPoint) points.get(index).clone();
				pt.setX(pt.getX() + offset.getX());
				pt.setY(pt.getY() + offset.getY());
				points.set(index, pt);
			}
		}
	}
}