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 }