View Javadoc

    1   /***
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.rules;
    5
    6   import net.sourceforge.pmd.AbstractRule;
    7   import net.sourceforge.pmd.ast.ASTBlockStatement;
    8   import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
    9   import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
    10  import net.sourceforge.pmd.ast.ASTEnumDeclaration;
    11  import net.sourceforge.pmd.ast.ASTForStatement;
    12  import net.sourceforge.pmd.ast.ASTIfStatement;
    13  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    14  import net.sourceforge.pmd.ast.ASTMethodDeclarator;
    15  import net.sourceforge.pmd.ast.ASTSwitchLabel;
    16  import net.sourceforge.pmd.ast.ASTSwitchStatement;
    17  import net.sourceforge.pmd.ast.ASTWhileStatement;
    18  import net.sourceforge.pmd.ast.Node;
    19  import net.sourceforge.pmd.ast.SimpleNode;
    20
    21  import java.util.Stack;
    22
    23  /***
    24   * @author Donald A. Leckie
    25   * @version $Revision: 1.11 $, $Date: 2006/03/29 13:58:50 $
    26   * @since January 14, 2003
    27   */
    28  public class CyclomaticComplexity extends AbstractRule {
    29
    30      private static class Entry {
    31          private SimpleNode node;
    32          private int decisionPoints = 1;
    33          public int highestDecisionPoints;
    34          public int methodCount;
    35
    36          private Entry(SimpleNode node) {
    37              this.node = node;
    38          }
    39
    40          public void bumpDecisionPoints() {
    41              decisionPoints++;
    42          }
    43
    44          public void bumpDecisionPoints(int size) {
    45              decisionPoints += size;
    46          }
    47
    48          public int getComplexityAverage() {
    49              return ((double) methodCount == 0) ? 1 : (int) (Math.rint((double) decisionPoints / (double) methodCount));
    50          }
    51      }
    52
    53      private Stack entryStack = new Stack();
    54
    55      public Object visit(ASTIfStatement node, Object data) {
    56          ((Entry) entryStack.peek()).bumpDecisionPoints();
    57          super.visit(node, data);
    58          return data;
    59      }
    60
    61      public Object visit(ASTForStatement node, Object data) {
    62          ((Entry) entryStack.peek()).bumpDecisionPoints();
    63          super.visit(node, data);
    64          return data;
    65      }
    66
    67      public Object visit(ASTSwitchStatement node, Object data) {
    68          Entry entry = (Entry) entryStack.peek();
    69          int childCount = node.jjtGetNumChildren();
    70          int lastIndex = childCount - 1;
    71          for (int n = 0; n < lastIndex; n++) {
    72              Node childNode = node.jjtGetChild(n);
    73              if (childNode instanceof ASTSwitchLabel) {
    74                  childNode = node.jjtGetChild(n + 1);
    75                  if (childNode instanceof ASTBlockStatement) {
    76                      entry.bumpDecisionPoints();
    77                  }
    78              }
    79          }
    80          super.visit(node, data);
    81          return data;
    82      }
    83
    84      public Object visit(ASTWhileStatement node, Object data) {
    85          ((Entry) entryStack.peek()).bumpDecisionPoints();
    86          super.visit(node, data);
    87          return data;
    88      }
    89
    90      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    91          if (node.isInterface()) {
    92              return data;
    93          }
    94
    95          entryStack.push(new Entry(node));
    96          super.visit(node, data);
    97          Entry classEntry = (Entry) entryStack.pop();
    98          if ((classEntry.getComplexityAverage() >= getIntProperty("reportLevel")) || (classEntry.highestDecisionPoints >= getIntProperty("reportLevel"))) {
    99              addViolation(data, node, new String[]{"class", node.getImage(), String.valueOf(classEntry.getComplexityAverage()) + " (Highest = " + String.valueOf(classEntry.highestDecisionPoints) + ")"});
    100         }
    101         return data;
    102     }
    103
    104     public Object visit(ASTMethodDeclaration node, Object data) {
    105         entryStack.push(new Entry(node));
    106         super.visit(node, data);
    107         Entry methodEntry = (Entry) entryStack.pop();
    108         int methodDecisionPoints = methodEntry.decisionPoints;
    109         Entry classEntry = (Entry) entryStack.peek();
    110         classEntry.methodCount++;
    111         classEntry.bumpDecisionPoints(methodDecisionPoints);
    112
    113         if (methodDecisionPoints > classEntry.highestDecisionPoints) {
    114             classEntry.highestDecisionPoints = methodDecisionPoints;
    115         }
    116
    117         ASTMethodDeclarator methodDeclarator = null;
    118         for (int n = 0; n < node.jjtGetNumChildren(); n++) {
    119             Node childNode = node.jjtGetChild(n);
    120             if (childNode instanceof ASTMethodDeclarator) {
    121                 methodDeclarator = (ASTMethodDeclarator) childNode;
    122                 break;
    123             }
    124         }
    125
    126         if (methodEntry.decisionPoints >= getIntProperty("reportLevel")) {
    127             addViolation(data, node, new String[]{"method", (methodDeclarator == null) ? "" : methodDeclarator.getImage(), String.valueOf(methodEntry.decisionPoints)});
    128         }
    129
    130         return data;
    131     }
    132
    133     public Object visit(ASTEnumDeclaration node, Object data) {
    134         entryStack.push(new Entry(node));
    135         super.visit(node, data);
    136         Entry classEntry = (Entry) entryStack.pop();
    137         if ((classEntry.getComplexityAverage() >= getIntProperty("reportLevel")) || (classEntry.highestDecisionPoints >= getIntProperty("reportLevel"))) {
    138             addViolation(data, node, new String[]{"class", node.getImage(), String.valueOf(classEntry.getComplexityAverage()) + "(Highest = " + String.valueOf(classEntry.highestDecisionPoints) + ")"});
    139         }
    140         return data;
    141     }
    142
    143     public Object visit(ASTConstructorDeclaration node, Object data) {
    144         entryStack.push(new Entry(node));
    145         super.visit(node, data);
    146         Entry constructorEntry = (Entry) entryStack.pop();
    147         int constructorDecisionPointCount = constructorEntry.decisionPoints;
    148         Entry classEntry = (Entry) entryStack.peek();
    149         classEntry.methodCount++;
    150         classEntry.decisionPoints += constructorDecisionPointCount;
    151         if (constructorDecisionPointCount > classEntry.highestDecisionPoints) {
    152             classEntry.highestDecisionPoints = constructorDecisionPointCount;
    153         }
    154         if (constructorEntry.decisionPoints >= getIntProperty("reportLevel")) {
    155             addViolation(data, node, new String[]{"constructor", classEntry.node.getImage(), String.valueOf(constructorDecisionPointCount)});
    156         }
    157         return data;
    158     }
    159
    160 }