package soot.toolkits.astmetrics;
import java.util.*;
import polyglot.ast.*;
import polyglot.util.CodeWriter;
import polyglot.visit.NodeVisitor;
public class StmtSumWeightedByDepth extends ASTMetric {
int currentDepth;
int sum;
int maxDepth;
int numNodes;
Stack<ArrayList> labelNodesSoFar = new Stack<ArrayList>();
ArrayList<Node> blocksWithAbruptFlow = new ArrayList<Node>();
HashMap<Node, Integer> stmtToMetric = new HashMap<Node, Integer>();
HashMap<Node, Integer> stmtToMetricDepth = new HashMap<Node, Integer>();
public static boolean tmpAbruptChecker = false;
public StmtSumWeightedByDepth(Node node){
super(node);
}
public void printAstMetric(Node n, CodeWriter w) {
if (n instanceof Stmt) {
if (stmtToMetric.containsKey(n)) {
w.write(" // sum= "+stmtToMetric.get(n)+" : depth= "+stmtToMetricDepth.get(n)+"\t");
}
}
}
public void reset() {
// if not one, then fields and method sigs don't get counted
currentDepth = 1; //inside a class
maxDepth = 1;
sum = 0;
numNodes = 0;
}
public void addMetrics(ClassData data) {
//data.addMetric(new MetricData("MaxDepth",new Integer(maxDepth)));
data.addMetric(new MetricData("D-W-Complexity",new Double(sum)));
data.addMetric(new MetricData("AST-Node-Count",new Integer(numNodes)));
}
private void increaseDepth(){
System.out.println("Increasing depth");
currentDepth++;
if(currentDepth > maxDepth)
maxDepth = currentDepth;
}
private void decreaseDepth(){
System.out.println("Decreasing depth");
currentDepth--;
}
/*
* List of Node types which increase depth of traversal!!!
* Any construct where one can have a { } increases the depth
* hence even though if(cond) stmt doesnt expicitly use a block
* its depth is still +1 when executing the stmt
*
* If the "if" stmt has code if(cond) { stmt } OR if(cond) stmt this will only increase the depth by 1 (ignores compound stmt blocks)
*
* If, Loop, Try, Synch, ProcDecl, Init, Switch, LocalClassDecl .... add currentDepth to sum and then increase depth by one
* irrespective of how many stmts there are in the body
*
* Block ... if it is a block within a block, add currentDepth plus increment depth ONLY if it has abrupt flow out of it.
*/
public NodeVisitor enter(Node parent, Node n){
numNodes++;
if (n instanceof CodeDecl) {
// maintain stack of label arrays (can't have label from inside method to outside)
labelNodesSoFar.push(new ArrayList());
}
else if (n instanceof Labeled) {
// add any labels we find to the array
labelNodesSoFar.peek().add(((Labeled)n).label());
}
if(n instanceof If || n instanceof Loop || n instanceof Try || n instanceof Switch
|| n instanceof LocalClassDecl || n instanceof Synchronized
|| n instanceof ProcedureDecl || n instanceof Initializer ){
sum += currentDepth*2;
System.out.println(n);
increaseDepth();
} else if (parent instanceof Block && n instanceof Block) {
StmtSumWeightedByDepth.tmpAbruptChecker = false;
n.visit(new NodeVisitor() {
// extended NodeVisitor that checks for branching out of a block
public NodeVisitor enter(Node parent, Node node){
if(node instanceof Branch) {
Branch b = (Branch)node;
// null branching out of a plain block is NOT ALLOWED!
if (b.label() != null && labelNodesSoFar.peek().contains(b.label()))
{
StmtSumWeightedByDepth.tmpAbruptChecker = true;
}
}
return enter(node);
}
// this method simply stops further node visiting if we found our info
public Node override(Node parent, Node node) {
if (StmtSumWeightedByDepth.tmpAbruptChecker)
return node;
return null;
}
});
if (StmtSumWeightedByDepth.tmpAbruptChecker)
{
blocksWithAbruptFlow.add(n);
sum += currentDepth*2;
System.out.println(n);
increaseDepth();
}
}
// switch from Stmt to Expr here, since Expr is the smallest unit
else if (n instanceof Expr || n instanceof Formal){
System.out.print(sum +" "+n+" ");
sum += currentDepth*2;
System.out.println(sum);
}
// carry metric cummulative for each statement for metricPrettyPrinter
if (n instanceof Stmt) {
stmtToMetric.put(n, new Integer(sum));
stmtToMetricDepth.put(n, new Integer(currentDepth));
}
return enter(n);
}
public Node leave(Node old, Node n, NodeVisitor v){
// stack maintenance, if leaving a method
if (n instanceof CodeDecl)
labelNodesSoFar.pop();
if(n instanceof If || n instanceof Loop || n instanceof Try || n instanceof Switch
|| n instanceof LocalClassDecl || n instanceof Synchronized
|| n instanceof ProcedureDecl || n instanceof Initializer ) {
decreaseDepth();
} else if (n instanceof Block && blocksWithAbruptFlow.contains(n)) {
decreaseDepth();
}
return n;
}
}