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.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.trace.MethodExecution;

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 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);
		add(mxgraphComponent, BorderLayout.CENTER);
	}

	/** 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()]));
	}

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

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

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

	/**
	 * Whether sourceCell parents contain targetCell
	 * @param sourceCell
	 * @param targetCell
	 * @return
	 */
	protected 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 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()));
	}

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