1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2017 the original author or authors. | |
4 | // | |
5 | // This library is free software; you can redistribute it and/or | |
6 | // modify it under the terms of the GNU Lesser General Public | |
7 | // License as published by the Free Software Foundation; either | |
8 | // version 2.1 of the License, or (at your option) any later version. | |
9 | // | |
10 | // This library is distributed in the hope that it will be useful, | |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | // Lesser General Public License for more details. | |
14 | // | |
15 | // You should have received a copy of the GNU Lesser General Public | |
16 | // License along with this library; if not, write to the Free Software | |
17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | //////////////////////////////////////////////////////////////////////////////// | |
19 | ||
20 | package com.puppycrawl.tools.checkstyle.checks.metrics; | |
21 | ||
22 | import java.util.ArrayDeque; | |
23 | import java.util.Deque; | |
24 | ||
25 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
26 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
27 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
28 | ||
29 | /** | |
30 | * This check calculates the Non Commenting Source Statements (NCSS) metric for | |
31 | * java source files and methods. The check adheres to the <a | |
32 | * href="http://www.kclee.com/clemens/java/javancss">JavaNCSS specification | |
33 | * </a> and gives the same results as the JavaNCSS tool. | |
34 | * | |
35 | * <p>The NCSS-metric tries to determine complexity of methods, classes and files | |
36 | * by counting the non commenting lines. Roughly said this is (nearly) | |
37 | * equivalent to counting the semicolons and opening curly braces. | |
38 | * | |
39 | * @author Lars Ködderitzsch | |
40 | */ | |
41 | // -@cs[AbbreviationAsWordInName] We can not change it as, | |
42 | // check's name is a part of API (used in configurations). | |
43 | public class JavaNCSSCheck extends AbstractCheck { | |
44 | ||
45 | /** | |
46 | * A key is pointing to the warning message text in "messages.properties" | |
47 | * file. | |
48 | */ | |
49 | public static final String MSG_METHOD = "ncss.method"; | |
50 | ||
51 | /** | |
52 | * A key is pointing to the warning message text in "messages.properties" | |
53 | * file. | |
54 | */ | |
55 | public static final String MSG_CLASS = "ncss.class"; | |
56 | ||
57 | /** | |
58 | * A key is pointing to the warning message text in "messages.properties" | |
59 | * file. | |
60 | */ | |
61 | public static final String MSG_FILE = "ncss.file"; | |
62 | ||
63 | /** Default constant for max file ncss. */ | |
64 | private static final int FILE_MAX_NCSS = 2000; | |
65 | ||
66 | /** Default constant for max file ncss. */ | |
67 | private static final int CLASS_MAX_NCSS = 1500; | |
68 | ||
69 | /** Default constant for max method ncss. */ | |
70 | private static final int METHOD_MAX_NCSS = 50; | |
71 | ||
72 | /** Maximum ncss for a complete source file. */ | |
73 | private int fileMaximum = FILE_MAX_NCSS; | |
74 | ||
75 | /** Maximum ncss for a class. */ | |
76 | private int classMaximum = CLASS_MAX_NCSS; | |
77 | ||
78 | /** Maximum ncss for a method. */ | |
79 | private int methodMaximum = METHOD_MAX_NCSS; | |
80 | ||
81 | /** List containing the stacked counters. */ | |
82 | private Deque<Counter> counters; | |
83 | ||
84 | @Override | |
85 | public int[] getDefaultTokens() { | |
86 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/JavaNCSSCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
87 | TokenTypes.CLASS_DEF, | |
88 | TokenTypes.INTERFACE_DEF, | |
89 | TokenTypes.METHOD_DEF, | |
90 | TokenTypes.CTOR_DEF, | |
91 | TokenTypes.INSTANCE_INIT, | |
92 | TokenTypes.STATIC_INIT, | |
93 | TokenTypes.PACKAGE_DEF, | |
94 | TokenTypes.IMPORT, | |
95 | TokenTypes.VARIABLE_DEF, | |
96 | TokenTypes.CTOR_CALL, | |
97 | TokenTypes.SUPER_CTOR_CALL, | |
98 | TokenTypes.LITERAL_IF, | |
99 | TokenTypes.LITERAL_ELSE, | |
100 | TokenTypes.LITERAL_WHILE, | |
101 | TokenTypes.LITERAL_DO, | |
102 | TokenTypes.LITERAL_FOR, | |
103 | TokenTypes.LITERAL_SWITCH, | |
104 | TokenTypes.LITERAL_BREAK, | |
105 | TokenTypes.LITERAL_CONTINUE, | |
106 | TokenTypes.LITERAL_RETURN, | |
107 | TokenTypes.LITERAL_THROW, | |
108 | TokenTypes.LITERAL_SYNCHRONIZED, | |
109 | TokenTypes.LITERAL_CATCH, | |
110 | TokenTypes.LITERAL_FINALLY, | |
111 | TokenTypes.EXPR, | |
112 | TokenTypes.LABELED_STAT, | |
113 | TokenTypes.LITERAL_CASE, | |
114 | TokenTypes.LITERAL_DEFAULT, | |
115 | }; | |
116 | } | |
117 | ||
118 | @Override | |
119 | public int[] getRequiredTokens() { | |
120 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/JavaNCSSCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
121 | TokenTypes.CLASS_DEF, | |
122 | TokenTypes.INTERFACE_DEF, | |
123 | TokenTypes.METHOD_DEF, | |
124 | TokenTypes.CTOR_DEF, | |
125 | TokenTypes.INSTANCE_INIT, | |
126 | TokenTypes.STATIC_INIT, | |
127 | TokenTypes.PACKAGE_DEF, | |
128 | TokenTypes.IMPORT, | |
129 | TokenTypes.VARIABLE_DEF, | |
130 | TokenTypes.CTOR_CALL, | |
131 | TokenTypes.SUPER_CTOR_CALL, | |
132 | TokenTypes.LITERAL_IF, | |
133 | TokenTypes.LITERAL_ELSE, | |
134 | TokenTypes.LITERAL_WHILE, | |
135 | TokenTypes.LITERAL_DO, | |
136 | TokenTypes.LITERAL_FOR, | |
137 | TokenTypes.LITERAL_SWITCH, | |
138 | TokenTypes.LITERAL_BREAK, | |
139 | TokenTypes.LITERAL_CONTINUE, | |
140 | TokenTypes.LITERAL_RETURN, | |
141 | TokenTypes.LITERAL_THROW, | |
142 | TokenTypes.LITERAL_SYNCHRONIZED, | |
143 | TokenTypes.LITERAL_CATCH, | |
144 | TokenTypes.LITERAL_FINALLY, | |
145 | TokenTypes.EXPR, | |
146 | TokenTypes.LABELED_STAT, | |
147 | TokenTypes.LITERAL_CASE, | |
148 | TokenTypes.LITERAL_DEFAULT, | |
149 | }; | |
150 | } | |
151 | ||
152 | @Override | |
153 | public int[] getAcceptableTokens() { | |
154 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/JavaNCSSCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
155 | TokenTypes.CLASS_DEF, | |
156 | TokenTypes.INTERFACE_DEF, | |
157 | TokenTypes.METHOD_DEF, | |
158 | TokenTypes.CTOR_DEF, | |
159 | TokenTypes.INSTANCE_INIT, | |
160 | TokenTypes.STATIC_INIT, | |
161 | TokenTypes.PACKAGE_DEF, | |
162 | TokenTypes.IMPORT, | |
163 | TokenTypes.VARIABLE_DEF, | |
164 | TokenTypes.CTOR_CALL, | |
165 | TokenTypes.SUPER_CTOR_CALL, | |
166 | TokenTypes.LITERAL_IF, | |
167 | TokenTypes.LITERAL_ELSE, | |
168 | TokenTypes.LITERAL_WHILE, | |
169 | TokenTypes.LITERAL_DO, | |
170 | TokenTypes.LITERAL_FOR, | |
171 | TokenTypes.LITERAL_SWITCH, | |
172 | TokenTypes.LITERAL_BREAK, | |
173 | TokenTypes.LITERAL_CONTINUE, | |
174 | TokenTypes.LITERAL_RETURN, | |
175 | TokenTypes.LITERAL_THROW, | |
176 | TokenTypes.LITERAL_SYNCHRONIZED, | |
177 | TokenTypes.LITERAL_CATCH, | |
178 | TokenTypes.LITERAL_FINALLY, | |
179 | TokenTypes.EXPR, | |
180 | TokenTypes.LABELED_STAT, | |
181 | TokenTypes.LITERAL_CASE, | |
182 | TokenTypes.LITERAL_DEFAULT, | |
183 | }; | |
184 | } | |
185 | ||
186 | @Override | |
187 | public void beginTree(DetailAST rootAST) { | |
188 | counters = new ArrayDeque<>(); | |
189 | ||
190 | //add a counter for the file | |
191 |
1
1. beginTree : removed call to java/util/Deque::push → KILLED |
counters.push(new Counter()); |
192 | } | |
193 | ||
194 | @Override | |
195 | public void visitToken(DetailAST ast) { | |
196 | final int tokenType = ast.getType(); | |
197 | ||
198 |
5
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED 3. visitToken : negated conditional → KILLED 4. visitToken : negated conditional → KILLED 5. visitToken : negated conditional → KILLED |
if (tokenType == TokenTypes.CLASS_DEF |
199 | || tokenType == TokenTypes.METHOD_DEF | |
200 | || tokenType == TokenTypes.CTOR_DEF | |
201 | || tokenType == TokenTypes.STATIC_INIT | |
202 | || tokenType == TokenTypes.INSTANCE_INIT) { | |
203 | //add a counter for this class/method | |
204 |
1
1. visitToken : removed call to java/util/Deque::push → KILLED |
counters.push(new Counter()); |
205 | } | |
206 | ||
207 | //check if token is countable | |
208 |
1
1. visitToken : negated conditional → KILLED |
if (isCountable(ast)) { |
209 | //increment the stacked counters | |
210 |
1
1. visitToken : removed call to java/util/Deque::forEach → KILLED |
counters.forEach(Counter::increment); |
211 | } | |
212 | } | |
213 | ||
214 | @Override | |
215 | public void leaveToken(DetailAST ast) { | |
216 | final int tokenType = ast.getType(); | |
217 |
4
1. leaveToken : negated conditional → KILLED 2. leaveToken : negated conditional → KILLED 3. leaveToken : negated conditional → KILLED 4. leaveToken : negated conditional → KILLED |
if (tokenType == TokenTypes.METHOD_DEF |
218 | || tokenType == TokenTypes.CTOR_DEF | |
219 | || tokenType == TokenTypes.STATIC_INIT | |
220 | || tokenType == TokenTypes.INSTANCE_INIT) { | |
221 | //pop counter from the stack | |
222 | final Counter counter = counters.pop(); | |
223 | ||
224 | final int count = counter.getCount(); | |
225 |
2
1. leaveToken : changed conditional boundary → KILLED 2. leaveToken : negated conditional → KILLED |
if (count > methodMaximum) { |
226 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/JavaNCSSCheck::log → KILLED |
log(ast.getLineNo(), ast.getColumnNo(), MSG_METHOD, |
227 | count, methodMaximum); | |
228 | } | |
229 | } | |
230 |
1
1. leaveToken : negated conditional → KILLED |
else if (tokenType == TokenTypes.CLASS_DEF) { |
231 | //pop counter from the stack | |
232 | final Counter counter = counters.pop(); | |
233 | ||
234 | final int count = counter.getCount(); | |
235 |
2
1. leaveToken : changed conditional boundary → KILLED 2. leaveToken : negated conditional → KILLED |
if (count > classMaximum) { |
236 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/JavaNCSSCheck::log → KILLED |
log(ast.getLineNo(), ast.getColumnNo(), MSG_CLASS, |
237 | count, classMaximum); | |
238 | } | |
239 | } | |
240 | } | |
241 | ||
242 | @Override | |
243 | public void finishTree(DetailAST rootAST) { | |
244 | //pop counter from the stack | |
245 | final Counter counter = counters.pop(); | |
246 | ||
247 | final int count = counter.getCount(); | |
248 |
2
1. finishTree : changed conditional boundary → KILLED 2. finishTree : negated conditional → KILLED |
if (count > fileMaximum) { |
249 |
1
1. finishTree : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/JavaNCSSCheck::log → KILLED |
log(rootAST.getLineNo(), rootAST.getColumnNo(), MSG_FILE, |
250 | count, fileMaximum); | |
251 | } | |
252 | } | |
253 | ||
254 | /** | |
255 | * Sets the maximum ncss for a file. | |
256 | * | |
257 | * @param fileMaximum | |
258 | * the maximum ncss | |
259 | */ | |
260 | public void setFileMaximum(int fileMaximum) { | |
261 | this.fileMaximum = fileMaximum; | |
262 | } | |
263 | ||
264 | /** | |
265 | * Sets the maximum ncss for a class. | |
266 | * | |
267 | * @param classMaximum | |
268 | * the maximum ncss | |
269 | */ | |
270 | public void setClassMaximum(int classMaximum) { | |
271 | this.classMaximum = classMaximum; | |
272 | } | |
273 | ||
274 | /** | |
275 | * Sets the maximum ncss for a method. | |
276 | * | |
277 | * @param methodMaximum | |
278 | * the maximum ncss | |
279 | */ | |
280 | public void setMethodMaximum(int methodMaximum) { | |
281 | this.methodMaximum = methodMaximum; | |
282 | } | |
283 | ||
284 | /** | |
285 | * Checks if a token is countable for the ncss metric. | |
286 | * | |
287 | * @param ast | |
288 | * the AST | |
289 | * @return true if the token is countable | |
290 | */ | |
291 | private static boolean isCountable(DetailAST ast) { | |
292 | boolean countable = true; | |
293 | ||
294 | final int tokenType = ast.getType(); | |
295 | ||
296 | //check if an expression is countable | |
297 |
1
1. isCountable : negated conditional → KILLED |
if (tokenType == TokenTypes.EXPR) { |
298 | countable = isExpressionCountable(ast); | |
299 | } | |
300 | //check if an variable definition is countable | |
301 |
1
1. isCountable : negated conditional → KILLED |
else if (tokenType == TokenTypes.VARIABLE_DEF) { |
302 | countable = isVariableDefCountable(ast); | |
303 | } | |
304 |
1
1. isCountable : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return countable; |
305 | } | |
306 | ||
307 | /** | |
308 | * Checks if a variable definition is countable. | |
309 | * | |
310 | * @param ast the AST | |
311 | * @return true if the variable definition is countable, false otherwise | |
312 | */ | |
313 | private static boolean isVariableDefCountable(DetailAST ast) { | |
314 | boolean countable = false; | |
315 | ||
316 | //count variable definitions only if they are direct child to a slist or | |
317 | // object block | |
318 | final int parentType = ast.getParent().getType(); | |
319 | ||
320 |
2
1. isVariableDefCountable : negated conditional → KILLED 2. isVariableDefCountable : negated conditional → KILLED |
if (parentType == TokenTypes.SLIST |
321 | || parentType == TokenTypes.OBJBLOCK) { | |
322 | final DetailAST prevSibling = ast.getPreviousSibling(); | |
323 | ||
324 | //is countable if no previous sibling is found or | |
325 | //the sibling is no COMMA. | |
326 | //This is done because multiple assignment on one line are counted | |
327 | // as 1 | |
328 |
1
1. isVariableDefCountable : negated conditional → KILLED |
countable = prevSibling == null |
329 |
1
1. isVariableDefCountable : negated conditional → KILLED |
|| prevSibling.getType() != TokenTypes.COMMA; |
330 | } | |
331 | ||
332 |
1
1. isVariableDefCountable : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return countable; |
333 | } | |
334 | ||
335 | /** | |
336 | * Checks if an expression is countable for the ncss metric. | |
337 | * | |
338 | * @param ast the AST | |
339 | * @return true if the expression is countable, false otherwise | |
340 | */ | |
341 | private static boolean isExpressionCountable(DetailAST ast) { | |
342 | final boolean countable; | |
343 | ||
344 | //count expressions only if they are direct child to a slist (method | |
345 | // body, for loop...) | |
346 | //or direct child of label,if,else,do,while,for | |
347 | final int parentType = ast.getParent().getType(); | |
348 | switch (parentType) { | |
349 | case TokenTypes.SLIST : | |
350 | case TokenTypes.LABELED_STAT : | |
351 | case TokenTypes.LITERAL_FOR : | |
352 | case TokenTypes.LITERAL_DO : | |
353 | case TokenTypes.LITERAL_WHILE : | |
354 | case TokenTypes.LITERAL_IF : | |
355 | case TokenTypes.LITERAL_ELSE : | |
356 | //don't count if or loop conditions | |
357 | final DetailAST prevSibling = ast.getPreviousSibling(); | |
358 |
1
1. isExpressionCountable : negated conditional → KILLED |
countable = prevSibling == null |
359 |
1
1. isExpressionCountable : negated conditional → KILLED |
|| prevSibling.getType() != TokenTypes.LPAREN; |
360 | break; | |
361 | default : | |
362 | countable = false; | |
363 | break; | |
364 | } | |
365 |
1
1. isExpressionCountable : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return countable; |
366 | } | |
367 | ||
368 | /** | |
369 | * Class representing a counter. | |
370 | * | |
371 | * @author Lars Ködderitzsch | |
372 | */ | |
373 | private static class Counter { | |
374 | /** The counters internal integer. */ | |
375 | private int count; | |
376 | ||
377 | /** | |
378 | * Increments the counter. | |
379 | */ | |
380 | public void increment() { | |
381 |
1
1. increment : Replaced integer addition with subtraction → KILLED |
count++; |
382 | } | |
383 | ||
384 | /** | |
385 | * Gets the counters value. | |
386 | * | |
387 | * @return the counter | |
388 | */ | |
389 | public int getCount() { | |
390 |
1
1. getCount : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return count; |
391 | } | |
392 | } | |
393 | } | |
Mutations | ||
86 |
1.1 |
|
120 |
1.1 |
|
154 |
1.1 |
|
191 |
1.1 |
|
198 |
1.1 2.2 3.3 4.4 5.5 |
|
204 |
1.1 |
|
208 |
1.1 |
|
210 |
1.1 |
|
217 |
1.1 2.2 3.3 4.4 |
|
225 |
1.1 2.2 |
|
226 |
1.1 |
|
230 |
1.1 |
|
235 |
1.1 2.2 |
|
236 |
1.1 |
|
248 |
1.1 2.2 |
|
249 |
1.1 |
|
297 |
1.1 |
|
301 |
1.1 |
|
304 |
1.1 |
|
320 |
1.1 2.2 |
|
328 |
1.1 |
|
329 |
1.1 |
|
332 |
1.1 |
|
358 |
1.1 |
|
359 |
1.1 |
|
365 |
1.1 |
|
381 |
1.1 |
|
390 |
1.1 |