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