package ai.cfg;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import ai.cfg.edges.CFGMultiTargetEdge;
import ai.cfg.edges.ConditionalEdge;
import ai.cfg.edges.ConstructorInvocationEdge;
import ai.cfg.edges.EmptyEdge;
import ai.cfg.edges.ExpressionStatementEdge;
import ai.cfg.edges.FinallyOrCatchEdge;
import ai.cfg.edges.InputEdge;
import ai.cfg.edges.NewVariableEdge;
import ai.cfg.edges.SuperConstructorInvocationEdge;
import ai.cfg.edges.SwitchEdge;
import ai.common.Pair;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
public class MethodControlFlowGraph {// nodes, edges
private final InterestingCodeFragment codeFragment;
private final CFGVertice start;
private final CFGVertice end;
private final Set<CFGVertice> vertices = new HashSet<CFGVertice>();
private final Set<CFGMultiTargetEdge> edges = new HashSet<CFGMultiTargetEdge>();
private final InputEdge inputEdge;
/**
* vertice -> set of nodes after directly after the vertice
*/
private final Multimap<CFGVertice, ASTNode> beforeVertice = HashMultimap.create();
/**
* vertice -> set of nodes after directly before the vertice
*/
private final Multimap<CFGVertice, ASTNode> afterVertice = HashMultimap.create();
private final Map<ASTNode, CFGVertice> afterNode = new HashMap<ASTNode, CFGVertice>();
private final Map<ASTNode, CFGVertice> beforeNode = new HashMap<ASTNode, CFGVertice>();
private final Map<CFGVertice, ASTNode> loopVertices = new HashMap<CFGVertice, ASTNode>();
public MethodControlFlowGraph(InterestingCodeFragment codeFragment) {
this.codeFragment = codeFragment;
this.start = new CFGVertice(codeFragment.getNode());
this.end = new CFGVertice(null);
this.inputEdge = new InputEdge(this.start, codeFragment);
edges.add(inputEdge);
this.vertices.add(start);
this.vertices.add(end);
this.start.addDescription("IN");
this.end.addDescription("OUT");
}
public InterestingCodeFragment getCodeFragment() {
return codeFragment;
}
public CFGMultiTargetEdge getInput() {
return this.inputEdge;
}
CFGVertice getStartVertice() {
return start;
}
public CFGVertice getEndVertice() {
return end;
}
private void assertGraphVertice(CFGVertice vertice) {
if (!vertices.contains(vertice))
throw new CFGException("Not a graph vertice: '%s'", vertice);
}
public void createVariableDeclarationEdge(CFGVertice previousVertice, CFGVertice nextVertice,
SimpleName name, Expression initializerOrNull, boolean asAssignment) {
assertGraphVertice(previousVertice);
assertGraphVertice(nextVertice);
CFGMultiTargetEdge edge = new NewVariableEdge(previousVertice, nextVertice, name, initializerOrNull, asAssignment);
previousVertice.addEdge(edge);
}
public void createConditionalEdge(CFGVertice previousVertice, CFGVertice positiveTarget,
CFGVertice negativeTarget, Expression conditionOrNull) {
assertGraphVertice(previousVertice);
assertGraphVertice(positiveTarget);
assertGraphVertice(negativeTarget);
CFGMultiTargetEdge edge = new ConditionalEdge(previousVertice, positiveTarget, negativeTarget, conditionOrNull);
previousVertice.addEdge(edge);
edges.add(edge);
}
public void createExceptionEdge(CFGVertice previousVertice, CFGVertice nextVertice,
SingleVariableDeclaration exceptionOrNull) {
assertGraphVertice(previousVertice);
assertGraphVertice(nextVertice);
CFGMultiTargetEdge edge = new FinallyOrCatchEdge(previousVertice, nextVertice, exceptionOrNull);
previousVertice.addEdge(edge);
edges.add(edge);
}
public CFGVertice addNewVertice(ASTNode node) {
CFGVertice result = new CFGVertice(node);
vertices.add(result);
return result;
}
public CFGVertice addNewVertice(ASTNode node, String description) {
CFGVertice result = new CFGVertice(node);
result.addDescription(description);
vertices.add(result);
return result;
}
public void createExpressionStatementEdge(CFGVertice previousVertice, CFGVertice nextVertice, Expression expression) {
assertGraphVertice(previousVertice);
assertGraphVertice(nextVertice);
CFGMultiTargetEdge edge = new ExpressionStatementEdge(previousVertice, nextVertice, expression);
previousVertice.addEdge(edge);
edges.add(edge);
}
public void createEmptyEdge(CFGVertice previousVertice, CFGVertice nextVertice) {
createEmptyEdge(previousVertice, nextVertice, new LinkedList<SimpleName>());
}
public void createEmptyEdge(CFGVertice previousVertice, CFGVertice nextVertice,
List<SimpleName> variablesToRemove) {
assertGraphVertice(previousVertice);
assertGraphVertice(nextVertice);
CFGMultiTargetEdge edge = new EmptyEdge(previousVertice, nextVertice, variablesToRemove);
previousVertice.addEdge(edge);
edges.add(edge);
}
public int size() {
return vertices.size();
}
public void createConstructorInvocationEdge(CFGVertice previousVertice, CFGVertice nextVertice,
ConstructorInvocation node) {
assertGraphVertice(previousVertice);
assertGraphVertice(nextVertice);
CFGMultiTargetEdge edge = new ConstructorInvocationEdge(previousVertice, nextVertice, node);
previousVertice.addEdge(edge);
edges.add(edge);
}
public void createSuperConstructorInvocationEdge(CFGVertice previousVertice, CFGVertice nextVertice,
SuperConstructorInvocation node) {
assertGraphVertice(previousVertice);
assertGraphVertice(nextVertice);
CFGMultiTargetEdge edge = new SuperConstructorInvocationEdge(previousVertice, nextVertice, node);
previousVertice.addEdge(edge);
edges.add(edge);
}
public void createSwitchEdge(CFGVertice previousVertice, CFGVertice defaultVertice, Expression expression,
List<Pair<CFGVertice, Expression>> cases) {
assertGraphVertice(previousVertice);
assertGraphVertice(defaultVertice);
for(Pair<CFGVertice, Expression> aCase: cases)
assertGraphVertice(aCase.left);
CFGMultiTargetEdge edge = new SwitchEdge(previousVertice, expression, defaultVertice, cases);
previousVertice.addEdge(edge);
edges.add(edge);
}
public void addMapping(ASTNode node, CFGVertice before, CFGVertice after) {
afterVertice.put(before, node);
beforeVertice.put(after, node);
CFGVertice x = afterNode.get(node);
if (x != null && x != after)
throw new RuntimeException("Duplicate after node vertice");
afterNode.put(node, after);
x = beforeNode.get(node);
if (x != null && x != before)
throw new RuntimeException("Duplicate after before vertice");
beforeNode.put(node, before);
}
public void addLoopInvariangMapping(ASTNode node, CFGVertice loopVertice){
loopVertices.put(loopVertice, node);
}
public Pair<Pair<Map<ASTNode, CFGVertice>, Multimap<CFGVertice, ASTNode>>, Pair<Map<ASTNode, CFGVertice>, Multimap<CFGVertice, ASTNode>>> getMappings(){
return Pair.create(Pair.create(beforeNode, beforeVertice), Pair.create(afterNode, afterVertice));
}
public Pair<Multimap<CFGVertice, ASTNode>, Multimap<CFGVertice, ASTNode>> getVerticeMappings(){
return Pair.create(beforeVertice, afterVertice);
}
public Map<CFGVertice, ASTNode> getLoopVerticesMapping(){
return loopVertices;
}
public Collection<CFGVertice> getVertices() {
return vertices;
}
}