package jetbrains.mps.ide.dataFlow.presentation; /*Generated by MPS */ import java.awt.Component; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap; import java.awt.Graphics; import java.util.Collections; import java.awt.event.MouseEvent; public class ControlFlowGraph<T extends IInstruction<T>> { private static final int MARGIN_X = 20; private static final int MARGIN_Y = 20; private static final int LINE_SEGMENT_SIZE = 40; protected final IGraphCreator<T> myGraphCreator; protected final IProgram<T> myProgram; protected Component myComponent; protected List<IBlock> myBlocks = new ArrayList<IBlock>(); protected List<Line> myLines = new ArrayList<Line>(); protected Set<ArrowHead> myArrowHeads = new HashSet<ArrowHead>(); private Map<Integer, FreeZone> myFreeZoneMap = new HashMap<Integer, FreeZone>(); /*package*/ int myMaxLineIndentRight; /*package*/ int myMaxLineIndentLeft; private int myWidth; private int myHeight; public ControlFlowGraph(IProgram<T> program, IGraphCreator<T> graphCreator) { this.myProgram = program; this.myGraphCreator = graphCreator; ControlFlowGraph.this.buildBlocks(); } public void buildBlocks() { Map<IInstruction<T>, IBlock> instructionToBlock = new HashMap<IInstruction<T>, IBlock>(); for (IInstruction<T> instruction : this.myProgram.getInstructions()) { IBlock block = this.myGraphCreator.createBlock((T) instruction, MARGIN_X, 0, 0, 0); this.myBlocks.add(block); instructionToBlock.put(instruction, block); } for (IInstruction<T> instruction : this.myProgram.getInstructions()) { IBlock block = instructionToBlock.get(instruction); Set<IBlock> succ = new HashSet<IBlock>(); for (IInstruction<T> instructionSucc : instruction.succ()) { succ.add(instructionToBlock.get(instructionSucc)); } block.setSucc(succ); } } public void paint(Graphics g) { for (IBlock block : this.myBlocks) { block.paint(g); } List<Line> lines = new ArrayList<Line>(this.myLines); Collections.sort(lines); for (Line line : lines) { line.paint(g); } for (ArrowHead arrowHead : this.myArrowHeads) { arrowHead.paint(g); } } public void setComponent(Component component) { this.myComponent = component; } public void relayout() { this.myLines.clear(); this.myArrowHeads.clear(); this.myMaxLineIndentRight = 0; this.myMaxLineIndentLeft = 0; this.myFreeZoneMap = new HashMap<Integer, FreeZone>(); for (IBlock block : this.myBlocks) { block.relayout(this.myComponent); } int maxWidth = 0; int maxHeight = 0; for (IBlock block : this.myBlocks) { maxWidth = Math.max(maxWidth, block.getWidth()); maxHeight = Math.max(maxHeight, block.getHeight()); } int y = MARGIN_Y; for (IBlock block : this.myBlocks) { block.setWidth(maxWidth); block.setX(MARGIN_X); block.setY(y); y += block.getHeight(); y += maxHeight / 2; } for (int i = 0; i < this.myBlocks.size(); i++) { IBlock block = this.myBlocks.get(i); for (IBlock succBlock : block.succ()) { if (this.myBlocks.indexOf(succBlock) == i + 1) { ControlFlowGraph.this.addSimpleLine(block, succBlock); } else { ControlFlowGraph.this.addAdditionalLine(block, succBlock); } } } ControlFlowGraph.this.shiftLeft(this.myMaxLineIndentLeft * LINE_SEGMENT_SIZE); this.myWidth = MARGIN_X * 2 + maxWidth + (this.myMaxLineIndentRight + this.myMaxLineIndentLeft) * LINE_SEGMENT_SIZE; this.myHeight = y + MARGIN_Y; } private void shiftLeft(int indent) { for (IBlock block : this.myBlocks) { block.setX(block.getX() + indent); } for (ArrowHead arrowHead : this.myArrowHeads) { arrowHead.setX(arrowHead.getX() + indent); } for (Line line : this.myLines) { line.shiftLeft(indent); } } protected void addAdditionalLine(IBlock startBlock, IBlock endBlock) { addAdditionalLine(startBlock, endBlock, new ControlFlowGraph.SimpleLineCreator()); } protected void addAdditionalLine(IBlock startBlock, IBlock endBlock, ControlFlowGraph.LineCreator lineCreator) { int startIndex = this.myBlocks.indexOf(startBlock); int endIndex = this.myBlocks.indexOf(endBlock); int rightIndent = 0; int leftIndent = 0; int first = Math.min(startIndex, endIndex); int last = Math.max(startIndex, endIndex); for (int i = 1; true; i++) { boolean canBeAdded = ControlFlowGraph.this.canBeAdded(first, last, i); if (canBeAdded) { rightIndent = i; break; } canBeAdded = ControlFlowGraph.this.canBeAdded(first, last, -i); if (canBeAdded) { leftIndent = i; break; } } this.myMaxLineIndentRight = Math.max(this.myMaxLineIndentRight, rightIndent); this.myMaxLineIndentLeft = Math.max(this.myMaxLineIndentLeft, leftIndent); int startBlockLevel = startBlock.getY() + startBlock.getHeight() / 2; int endBlockLevel = endBlock.getY() + endBlock.getHeight() / 2; if (rightIndent != 0) { int startBlockExit = startBlock.getX() + startBlock.getWidth(); int endBlockExit = endBlock.getX() + endBlock.getWidth(); this.myLines.add(lineCreator.createLine(startBlockExit, startBlockExit + LINE_SEGMENT_SIZE * rightIndent, startBlockLevel, LineDirection.HORIZONTAL)); this.myLines.add(lineCreator.createLine(endBlockExit, endBlockExit + LINE_SEGMENT_SIZE * rightIndent, endBlockLevel, LineDirection.HORIZONTAL)); this.myLines.add(lineCreator.createLine(startBlockLevel, endBlockLevel, startBlockExit + LINE_SEGMENT_SIZE * rightIndent, LineDirection.VERTICAL)); this.myArrowHeads.add(createArrowHead(endBlockExit, endBlockLevel, ArrowHeadDirection.LEFT)); } else if (leftIndent != 0) { int startBlockExit = startBlock.getX(); int endBlockExit = endBlock.getX(); this.myLines.add(lineCreator.createLine(startBlockExit, startBlockExit - LINE_SEGMENT_SIZE * leftIndent, startBlockLevel, LineDirection.HORIZONTAL)); this.myLines.add(lineCreator.createLine(endBlockExit, endBlockExit - LINE_SEGMENT_SIZE * leftIndent, endBlockLevel, LineDirection.HORIZONTAL)); this.myLines.add(lineCreator.createLine(startBlockLevel, endBlockLevel, startBlockExit - LINE_SEGMENT_SIZE * leftIndent, LineDirection.VERTICAL)); this.myArrowHeads.add(createArrowHead(endBlockExit, endBlockLevel, ArrowHeadDirection.RIGHT)); } } private boolean canBeAdded(int first, int last, int i) { FreeZone freeZone = this.myFreeZoneMap.get(i); if (freeZone == null) { freeZone = new FreeZone(0, this.myBlocks.size()); this.myFreeZoneMap.put(i, freeZone); } boolean canBeAdded = freeZone.canBeAdded(first, last); return canBeAdded; } protected void addSimpleLine(IBlock block, IBlock nextBlock) { addSimpleLine(block, nextBlock, new ControlFlowGraph.SimpleLineCreator()); } protected void addSimpleLine(IBlock block, IBlock nextBlock, ControlFlowGraph.LineCreator lineCreator) { int levelX = block.getX() + block.getWidth() / 2; int y1 = block.getY() + block.getHeight(); int y2 = nextBlock.getY(); this.myLines.add(lineCreator.createLine(y1, y2, levelX, LineDirection.VERTICAL)); this.myArrowHeads.add(createArrowHead(levelX, y2, ArrowHeadDirection.DOWN)); } protected ArrowHead createArrowHead(int x, int y, ArrowHeadDirection direction) { return new ArrowHead(x, y, direction); } public int getWidth() { return this.myWidth; } public int getHeight() { return this.myHeight; } public void addBlockListener(IBlockListener listener) { for (IBlock block : this.myBlocks) { block.addBlockListener(listener); } } public void removeBlockListener(IBlockListener listener) { for (IBlock block : this.myBlocks) { block.removeBlockListener(listener); } } public void processMousePressed(MouseEvent event) { for (IBlock block : this.myBlocks) { if (block.processMousePressed(event)) { return; } } } protected interface LineCreator { Line createLine(int first, int second, int level, LineDirection direction); } private class SimpleLineCreator implements ControlFlowGraph.LineCreator { public Line createLine(int first, int second, int level, LineDirection direction) { return new Line(first, second, level, direction); } } }