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