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 }