使用Java中的ASTParser实现Metric Suite

时间:2015-03-13 18:33:11

标签: java eclipse abstract-syntax-tree cyclomatic-complexity

背景 - 下面的问题

我刚开始在Java for Java中实现一个度量套件,但我担心我的方法不合适。

目前我正在为目录中的每个文件使用JDT ASTParser。这开始很好,我能够收集行数和每个类的每个方法的平均行数。这是通过一个MethodVisitor类完成的,该类扩展了ASTVisitor并包含一个方法访问(MethodDeclaration节点)。

我现在正在尝试为每种方法计算Cyclomatic Complexity。我已经拆分了方法体并且有一个ComplexityVisitor,它包含一个访问(IfStatement节点)和一个访问(ReturnStatement节点)。

使用这个结构我知道代码中有一个if语句,但我不确定如何知道有多少级别的“if else”。我能找到的唯一有用的方法是node.getElseStatement(),但这会返回一个基本上(或者在我看来)字符串的内容,因此必须使用正则表达式来知道语句可以采用的路径数。

所以我的问题是:

使用eclipses ASTParser时,有没有办法推断“if - else if - else”语句中有多少级别?

我是否应该寻找更清洁的解决方案,例如IJavaElement,或者自己解析代码,将关键字放到列表中然后循环回来。

一些示例代码 - 非常多地处于测试阶段

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

干杯

1 个答案:

答案 0 :(得分:2)

我不确定这是否能解答您的问题,但是为了计算McCabe的Cyclomatic Complexity(McCC)指标,您不需要关心if-else-if嵌套级别。你只需要计算&#34;分支&#34;的数量。说明并最后添加1。请参阅User's Guide工具的SourceMeter中的定义:

McCabe的Cyclomatic Complexity(McCC) 方法:方法的复杂性表示为其中的独立控制流路径的数量。它表示源代码中可能的执行路径数的下限,同时它是实现完整分支测试覆盖所需的最小测试用例数的上限。度量的值计算为以下指令的数量加1:if,for,foreach,while,do-while,case标签(属于switch指令),catch,条件语句(?:)。此外, 逻辑“和”(&amp;&amp;)和逻辑“或”(||)表达式也将值加1,因为它们的短路评估可能会导致分支,具体取决于第一个操作数。不包括以下说明:else,switch,default label(属于switch指令),try,finally。