| 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 |