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.ASTConstructorDeclaration;
11 import net.sourceforge.pmd.ast.ASTForStatement;
12 import net.sourceforge.pmd.ast.ASTIfStatement;
13 import net.sourceforge.pmd.ast.ASTInterfaceDeclaration;
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.ASTUnmodifiedClassDeclaration;
19 import net.sourceforge.pmd.ast.ASTWhileStatement;
20 import net.sourceforge.pmd.ast.Node;
21 import net.sourceforge.pmd.ast.SimpleNode;
22
23 import java.text.MessageFormat;
24 import java.util.Stack;
25
26 /***
27 *
28 * @author Donald A. Leckie
29 * @since January 14, 2003
30 * @version $Revision: 1.10 $, $Date: 2003/11/20 15:10:19 $
31 */
32 public class CyclomaticComplexityRule extends AbstractRule {
33 private Stack m_entryStack = new Stack();
34
35 /***
36 **************************************************************************
37 *
38 * @param node
39 * @param data
40 *
41 * @return
42 */
43 public Object visit(ASTIfStatement node, Object data) {
44 Entry entry = (Entry) m_entryStack.peek();
45 entry.m_decisionPoints++;
46 super.visit(node, data);
47
48 return data;
49 }
50
51 /***
52 **************************************************************************
53 *
54 * @param node
55 * @param data
56 *
57 * @return
58 */
59 public Object visit(ASTForStatement node, Object data) {
60 Entry entry = (Entry) m_entryStack.peek();
61 entry.m_decisionPoints++;
62 super.visit(node, data);
63
64 return data;
65 }
66
67 /***
68 **************************************************************************
69 *
70 * @param node
71 * @param data
72 *
73 * @return
74 */
75 public Object visit(ASTSwitchStatement node, Object data) {
76 Entry entry = (Entry) m_entryStack.peek();
77
78 int childCount = node.jjtGetNumChildren();
79 int lastIndex = childCount - 1;
80
81 for (int n = 0; n < lastIndex; n++) {
82 Node childNode = node.jjtGetChild(n);
83
84 if (childNode instanceof ASTSwitchLabel) {
85 childNode = node.jjtGetChild(n + 1);
86
87 if (childNode instanceof ASTBlockStatement) {
88 entry.m_decisionPoints++;
89 }
90 }
91 }
92
93 super.visit(node, data);
94
95 return data;
96 }
97
98 /***
99 **************************************************************************
100 *
101 * @param node
102 * @param data
103 *
104 * @return
105 */
106 public Object visit(ASTWhileStatement node, Object data) {
107 Entry entry = (Entry) m_entryStack.peek();
108 entry.m_decisionPoints++;
109 super.visit(node, data);
110
111 return data;
112 }
113
114 /***
115 **************************************************************************
116 *
117 * @param node
118 * @param data
119 *
120 * @return
121 */
122 public Object visit(ASTUnmodifiedClassDeclaration node, Object data) {
123 m_entryStack.push(new Entry(node));
124 super.visit(node, data);
125 Entry classEntry = (Entry) m_entryStack.pop();
126 double decisionPoints = (double) classEntry.m_decisionPoints;
127 double methodCount = (double) classEntry.m_methodCount;
128 int complexityAverage = (methodCount == 0) ? 1 : (int) (Math.rint(decisionPoints / methodCount));
129
130 if ((complexityAverage >= getIntProperty("reportLevel")) || (classEntry.m_highestDecisionPoints >= getIntProperty("reportLevel"))) {
131 // The {0} "{1}" has a cyclomatic complexity of {2}.
132 RuleContext ruleContext = (RuleContext) data;
133 String template = getMessage();
134 String className = node.getImage();
135 String complexityHighest = String.valueOf(classEntry.m_highestDecisionPoints);
136 String complexity = String.valueOf(complexityAverage) + " (Highest = " + complexityHighest + ")";
137 String[] args = {"class", className, complexity};
138 String message = MessageFormat.format(template, args);
139 int lineNumber = node.getBeginLine();
140 RuleViolation ruleViolation = createRuleViolation(ruleContext, lineNumber, message);
141 ruleContext.getReport().addRuleViolation(ruleViolation);
142 }
143
144 return data;
145 }
146
147 /***
148 **************************************************************************
149 *
150 * @param node
151 * @param data
152 *
153 * @return
154 */
155 public Object visit(ASTMethodDeclaration node, Object data) {
156 Node parentNode = node.jjtGetParent();
157
158 while (parentNode != null) {
159 if (parentNode instanceof ASTInterfaceDeclaration) {
160 return data;
161 }
162
163 parentNode = parentNode.jjtGetParent();
164 }
165
166 m_entryStack.push(new Entry(node));
167 super.visit(node, data);
168 Entry methodEntry = (Entry) m_entryStack.pop();
169 int methodDecisionPoints = methodEntry.m_decisionPoints;
170 Entry classEntry = (Entry) m_entryStack.peek();
171 classEntry.m_methodCount++;
172 classEntry.m_decisionPoints += methodDecisionPoints;
173
174 if (methodDecisionPoints > classEntry.m_highestDecisionPoints) {
175 classEntry.m_highestDecisionPoints = methodDecisionPoints;
176 }
177
178 ASTMethodDeclarator methodDeclarator = null;
179
180 for (int n = 0; n < node.jjtGetNumChildren(); n++) {
181 Node childNode = node.jjtGetChild(n);
182
183 if (childNode instanceof ASTMethodDeclarator) {
184 methodDeclarator = (ASTMethodDeclarator) childNode;
185 break;
186 }
187 }
188
189 if (methodEntry.m_decisionPoints >= getIntProperty("reportLevel")) {
190 // The {0} "{1}" has a cyclomatic complexity of {2}.
191 RuleContext ruleContext = (RuleContext) data;
192 String template = getMessage();
193 String methodName = (methodDeclarator == null) ? "" : methodDeclarator.getImage();
194 String complexity = String.valueOf(methodEntry.m_decisionPoints);
195 String[] args = {"method", methodName, complexity};
196 String message = MessageFormat.format(template, args);
197 int lineNumber = node.getBeginLine();
198 RuleViolation ruleViolation = createRuleViolation(ruleContext, lineNumber, message);
199 ruleContext.getReport().addRuleViolation(ruleViolation);
200 }
201
202 return data;
203 }
204
205 /***
206 **************************************************************************
207 *
208 * @param node
209 * @param data
210 *
211 * @return
212 */
213 public Object visit(ASTConstructorDeclaration node, Object data) {
214 m_entryStack.push(new Entry(node));
215 super.visit(node, data);
216 Entry constructorEntry = (Entry) m_entryStack.pop();
217 int constructorDecisionPointCount = constructorEntry.m_decisionPoints;
218 Entry classEntry = (Entry) m_entryStack.peek();
219 classEntry.m_methodCount++;
220 classEntry.m_decisionPoints += constructorDecisionPointCount;
221
222 if (constructorDecisionPointCount > classEntry.m_highestDecisionPoints) {
223 classEntry.m_highestDecisionPoints = constructorDecisionPointCount;
224 }
225
226 if (constructorEntry.m_decisionPoints >= getIntProperty("reportLevel")) {
227 // The {0} "{1}" has a cyclomatic complexity of {2}.
228 RuleContext ruleContext = (RuleContext) data;
229 String template = getMessage();
230 String constructorName = classEntry.m_node.getImage();
231 String complexity = String.valueOf(constructorDecisionPointCount);
232 String[] args = {"constructor", constructorName, complexity};
233 String message = MessageFormat.format(template, args);
234 int lineNumber = node.getBeginLine();
235 RuleViolation ruleViolation = createRuleViolation(ruleContext, lineNumber, message);
236 ruleContext.getReport().addRuleViolation(ruleViolation);
237 }
238
239 return data;
240 }
241
242 /***
243 ***************************************************************************
244 ***************************************************************************
245 ***************************************************************************
246 */
247 private class Entry {
248 // ASTUnmodifedClassDeclaration or ASTMethodDeclarator or ASTConstructorDeclaration
249 private SimpleNode m_node;
250 public int m_decisionPoints = 1;
251 public int m_highestDecisionPoints;
252 public int m_methodCount;
253
254 /***
255 ***********************************************************************
256 *
257 * @param node
258 */
259 private Entry(SimpleNode node) {
260 m_node = node;
261 }
262 }
263 }
This page was automatically generated by Maven