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