Background - Question below
I am at the start of implementing a metric suite in Java for Java however I am concerned that my approach is not appropriate.
Currently I am using the JDT's ASTParser for every file within a directory. This started off well and I was able to collect things around line count and average lines per method for each class. This was done via a MethodVisitor class which extends ASTVisitor and contains a method visit(MethodDeclaration node).
I am now trying to calculate Cyclomatic Complexity for every method. I have split out the method body and have a ComplexityVisitor which contains a visit(IfStatement node) and a visit(ReturnStatement node).
Using this structure I know that there is a if statement within the code but I am unsure on how to know how many levels of "if else"s there are. The only method I can find that is helpful is the node.getElseStatement() but this returns what is basically (or seems to me) a string and would therefore have to use regex to know the number of paths the statement could take.
So my question is:
Is there a way to deduce how many levels are in the "if - else if - else" statement when using eclipses ASTParser?
or
Should I be looking for a cleaner solution such as IJavaElement or parsing the code myself putting key words onto a list then looping back through them.
Some sample Code - very much in testing phase
public class Test {
private static List<ClassInfo> klasses = new ArrayList<ClassInfo>();
// Called for every file where str is what the file contains
public static void parse(String str) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(str.toCharArray());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
final CompilationUnit cu = (CompilationUnit) parser.createAST(null);
ClassVisitor cv = new ClassVisitor();
cu.accept(cv);
MethodVisitor methodsVisitor = new MethodVisitor(cu);
cu.accept(methodsVisitor);
ClassInfo klass = new ClassInfo(cv.getClassName(),
cu.getLineNumber(cu.getLength() - 1),
methodsVisitor.getNumberOfMethods(),
methodsVisitor.getAverageLinesPerMethod(),
methodsVisitor.getMethods());
for(int i = 0; i < klass.methods.size(); i++){
parser.setSource(klass.methods.get(i).body.toCharArray());
CyclomaticComplexityVisitor ccv = new CyclomaticComplexityVisitor();
cu.accept(ccv);
}
klasses.add(klass);
}
-
public class MethodVisitor extends ASTVisitor {
private CompilationUnit cu;
private int numberOfMethods;
private int lineCount;
private List<MethodInfo> methods = new ArrayList<MethodInfo>();
public MethodVisitor(CompilationUnit cu){
this.cu = cu;
}
public boolean visit(MethodDeclaration node){
int startPos = cu.getLineNumber(node.getStartPosition());
int endPos = cu.getLineNumber(node.getStartPosition() + node.getLength());
lineCount += (endPos - startPos);
numberOfMethods++;
String methodBody = node.getBody().toString();
MethodInfo m = new MethodInfo(node.getName().getIdentifier(),
(endPos - startPos),
node.getReturnType2());
m.body = methodBody;
methods.add(m);
return true;
}
-
public class CyclomaticComplexityVisitor extends ASTVisitor {
private int complexityScore = 0;
private int edges = 0;
private int nodes = 0;
private int exitPoints = 1;
private boolean firstReturn = true;
public boolean visit(IfStatement node){
System.out.println("THERE WAS AN IF");
String statement = node.toString();
System.out.println(statement);
return true;
}
public boolean visit(ReturnStatement node){
if (firstReturn) {
firstReturn = false;
} else {
exitPoints++;
}
return true;
}
Cheers