1 | /////////////////////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code and other text files for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2022 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.sizes; | |
21 | ||
22 | import java.util.ArrayDeque; | |
23 | import java.util.Deque; | |
24 | import java.util.EnumMap; | |
25 | import java.util.Map; | |
26 | ||
27 | import com.puppycrawl.tools.checkstyle.FileStatefulCheck; | |
28 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
29 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
30 | import com.puppycrawl.tools.checkstyle.api.Scope; | |
31 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
32 | import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; | |
33 | ||
34 | /** | |
35 | * <p> | |
36 | * Checks the number of methods declared in each type declaration by access modifier | |
37 | * or total count. | |
38 | * </p> | |
39 | * <p> | |
40 | * This check can be configured to flag classes that define too many methods | |
41 | * to prevent the class from getting too complex. Counting can be customized | |
42 | * to prevent too many total methods in a type definition ({@code maxTotal}), | |
43 | * or to prevent too many methods of a specific access modifier ({@code private}, | |
44 | * {@code package}, {@code protected} or {@code public}). Each count is completely | |
45 | * separated to customize how many methods of each you want to allow. For example, | |
46 | * specifying a {@code maxTotal} of 10, still means you can prevent more than 0 | |
47 | * {@code maxPackage} methods. A violation won't appear for 8 public methods, | |
48 | * but one will appear if there is also 3 private methods or any package-private methods. | |
49 | * </p> | |
50 | * <p> | |
51 | * Methods defined in anonymous classes are not counted towards any totals. | |
52 | * Counts only go towards the main type declaration parent, and are kept separate | |
53 | * from it's children's inner types. | |
54 | * </p> | |
55 | * <pre> | |
56 | * public class ExampleClass { | |
57 | * public enum Colors { | |
58 | * RED, GREEN, YELLOW; | |
59 | * | |
60 | * public String getRGB() { ... } // NOT counted towards ExampleClass | |
61 | * } | |
62 | * | |
63 | * public void example() { // counted towards ExampleClass | |
64 | * Runnable r = (new Runnable() { | |
65 | * public void run() { ... } // NOT counted towards ExampleClass, won't produce any violations | |
66 | * }); | |
67 | * } | |
68 | * | |
69 | * public static class InnerExampleClass { | |
70 | * protected void example2() { ... } // NOT counted towards ExampleClass, | |
71 | * // but counted towards InnerExampleClass | |
72 | * } | |
73 | * } | |
74 | * </pre> | |
75 | * <ul> | |
76 | * <li> | |
77 | * Property {@code maxTotal} - Specify the maximum number of methods allowed at all scope levels. | |
78 | * Type is {@code int}. | |
79 | * Default value is {@code 100}. | |
80 | * </li> | |
81 | * <li> | |
82 | * Property {@code maxPrivate} - Specify the maximum number of {@code private} methods allowed. | |
83 | * Type is {@code int}. | |
84 | * Default value is {@code 100}. | |
85 | * </li> | |
86 | * <li> | |
87 | * Property {@code maxPackage} - Specify the maximum number of {@code package} methods allowed. | |
88 | * Type is {@code int}. | |
89 | * Default value is {@code 100}. | |
90 | * </li> | |
91 | * <li> | |
92 | * Property {@code maxProtected} - Specify the maximum number of {@code protected} methods allowed. | |
93 | * Type is {@code int}. | |
94 | * Default value is {@code 100}. | |
95 | * </li> | |
96 | * <li> | |
97 | * Property {@code maxPublic} - Specify the maximum number of {@code public} methods allowed. | |
98 | * Type is {@code int}. | |
99 | * Default value is {@code 100}. | |
100 | * </li> | |
101 | * <li> | |
102 | * Property {@code tokens} - tokens to check | |
103 | * Type is {@code java.lang.String[]}. | |
104 | * Validation type is {@code tokenSet}. | |
105 | * Default value is: | |
106 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> | |
107 | * CLASS_DEF</a>, | |
108 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF"> | |
109 | * ENUM_CONSTANT_DEF</a>, | |
110 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> | |
111 | * ENUM_DEF</a>, | |
112 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> | |
113 | * INTERFACE_DEF</a>, | |
114 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> | |
115 | * ANNOTATION_DEF</a>, | |
116 | * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> | |
117 | * RECORD_DEF</a>. | |
118 | * </li> | |
119 | * </ul> | |
120 | * <p> | |
121 | * To configure the default check: | |
122 | * </p> | |
123 | * <pre> | |
124 | * <module name="MethodCount"/> | |
125 | * </pre> | |
126 | * <p> | |
127 | * To configure the check to allow no more than 30 methods per type declaration: | |
128 | * </p> | |
129 | * <pre> | |
130 | * <module name="MethodCount"> | |
131 | * <property name="maxTotal" value="30"/> | |
132 | * </module> | |
133 | * </pre> | |
134 | * <p> | |
135 | * To configure the check to allow no more than 10 public methods per type declaration, | |
136 | * and 40 methods in total: | |
137 | * </p> | |
138 | * <pre> | |
139 | * <module name="MethodCount"> | |
140 | * <property name="maxPublic" value="10"/> | |
141 | * <property name="maxTotal" value="40"/> | |
142 | * </module> | |
143 | * </pre> | |
144 | * <p> | |
145 | * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} | |
146 | * </p> | |
147 | * <p> | |
148 | * Violation Message Keys: | |
149 | * </p> | |
150 | * <ul> | |
151 | * <li> | |
152 | * {@code too.many.methods} | |
153 | * </li> | |
154 | * <li> | |
155 | * {@code too.many.packageMethods} | |
156 | * </li> | |
157 | * <li> | |
158 | * {@code too.many.privateMethods} | |
159 | * </li> | |
160 | * <li> | |
161 | * {@code too.many.protectedMethods} | |
162 | * </li> | |
163 | * <li> | |
164 | * {@code too.many.publicMethods} | |
165 | * </li> | |
166 | * </ul> | |
167 | * | |
168 | * @since 5.3 | |
169 | */ | |
170 | @FileStatefulCheck | |
171 | public final class MethodCountCheck extends AbstractCheck { | |
172 | ||
173 | /** | |
174 | * A key is pointing to the warning message text in "messages.properties" | |
175 | * file. | |
176 | */ | |
177 | public static final String MSG_PRIVATE_METHODS = "too.many.privateMethods"; | |
178 | ||
179 | /** | |
180 | * A key is pointing to the warning message text in "messages.properties" | |
181 | * file. | |
182 | */ | |
183 | public static final String MSG_PACKAGE_METHODS = "too.many.packageMethods"; | |
184 | ||
185 | /** | |
186 | * A key is pointing to the warning message text in "messages.properties" | |
187 | * file. | |
188 | */ | |
189 | public static final String MSG_PROTECTED_METHODS = "too.many.protectedMethods"; | |
190 | ||
191 | /** | |
192 | * A key is pointing to the warning message text in "messages.properties" | |
193 | * file. | |
194 | */ | |
195 | public static final String MSG_PUBLIC_METHODS = "too.many.publicMethods"; | |
196 | ||
197 | /** | |
198 | * A key is pointing to the warning message text in "messages.properties" | |
199 | * file. | |
200 | */ | |
201 | public static final String MSG_MANY_METHODS = "too.many.methods"; | |
202 | ||
203 | /** Default maximum number of methods. */ | |
204 | private static final int DEFAULT_MAX_METHODS = 100; | |
205 | ||
206 | /** Maintains stack of counters, to support inner types. */ | |
207 |
1
1. <init> : removed call to java/util/ArrayDeque::<init> → KILLED |
private final Deque<MethodCounter> counters = new ArrayDeque<>(); |
208 | ||
209 | /** Specify the maximum number of {@code private} methods allowed. */ | |
210 | private int maxPrivate = DEFAULT_MAX_METHODS; | |
211 | /** Specify the maximum number of {@code package} methods allowed. */ | |
212 | private int maxPackage = DEFAULT_MAX_METHODS; | |
213 | /** Specify the maximum number of {@code protected} methods allowed. */ | |
214 | private int maxProtected = DEFAULT_MAX_METHODS; | |
215 | /** Specify the maximum number of {@code public} methods allowed. */ | |
216 | private int maxPublic = DEFAULT_MAX_METHODS; | |
217 | /** Specify the maximum number of methods allowed at all scope levels. */ | |
218 | private int maxTotal = DEFAULT_MAX_METHODS; | |
219 | ||
220 | @Override | |
221 | public int[] getDefaultTokens() { | |
222 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getAcceptableTokens(); |
223 | } | |
224 | ||
225 | @Override | |
226 | public int[] getAcceptableTokens() { | |
227 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
228 | TokenTypes.CLASS_DEF, | |
229 | TokenTypes.ENUM_CONSTANT_DEF, | |
230 | TokenTypes.ENUM_DEF, | |
231 | TokenTypes.INTERFACE_DEF, | |
232 | TokenTypes.ANNOTATION_DEF, | |
233 | TokenTypes.METHOD_DEF, | |
234 | TokenTypes.RECORD_DEF, | |
235 | }; | |
236 | } | |
237 | ||
238 | @Override | |
239 | public int[] getRequiredTokens() { | |
240 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] {TokenTypes.METHOD_DEF}; |
241 | } | |
242 | ||
243 | @Override | |
244 | public void visitToken(DetailAST ast) { | |
245 |
3
1. visitToken : negated conditional → KILLED 2. visitToken : removed conditional - replaced equality check with false → KILLED 3. visitToken : removed conditional - replaced equality check with true → KILLED |
if (ast.getType() == TokenTypes.METHOD_DEF) { |
246 |
3
1. visitToken : negated conditional → KILLED 2. visitToken : removed conditional - replaced equality check with false → KILLED 3. visitToken : removed conditional - replaced equality check with true → KILLED |
if (isInLatestScopeDefinition(ast)) { |
247 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::raiseCounter → KILLED |
raiseCounter(ast); |
248 | } | |
249 | } | |
250 | else { | |
251 |
2
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck$MethodCounter::<init> → KILLED 2. visitToken : removed call to java/util/Deque::push → KILLED |
counters.push(new MethodCounter(ast)); |
252 | } | |
253 | } | |
254 | ||
255 | @Override | |
256 | public void leaveToken(DetailAST ast) { | |
257 |
3
1. leaveToken : negated conditional → KILLED 2. leaveToken : removed conditional - replaced equality check with false → KILLED 3. leaveToken : removed conditional - replaced equality check with true → KILLED |
if (ast.getType() != TokenTypes.METHOD_DEF) { |
258 | final MethodCounter counter = counters.pop(); | |
259 | ||
260 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::checkCounters → KILLED |
checkCounters(counter, ast); |
261 | } | |
262 | } | |
263 | ||
264 | /** | |
265 | * Checks if there is a scope definition to check and that the method is found inside that scope | |
266 | * (class, enum, etc.). | |
267 | * | |
268 | * @param methodDef | |
269 | * The method to analyze. | |
270 | * @return {@code true} if the method is part of the latest scope definition and should be | |
271 | * counted. | |
272 | */ | |
273 | private boolean isInLatestScopeDefinition(DetailAST methodDef) { | |
274 | boolean result = false; | |
275 | ||
276 |
3
1. isInLatestScopeDefinition : negated conditional → KILLED 2. isInLatestScopeDefinition : removed conditional - replaced equality check with false → KILLED 3. isInLatestScopeDefinition : removed conditional - replaced equality check with true → KILLED |
if (!counters.isEmpty()) { |
277 | final DetailAST latestDefinition = counters.peek().getScopeDefinition(); | |
278 | ||
279 |
3
1. isInLatestScopeDefinition : negated conditional → KILLED 2. isInLatestScopeDefinition : removed conditional - replaced equality check with false → KILLED 3. isInLatestScopeDefinition : removed conditional - replaced equality check with true → KILLED |
result = latestDefinition == methodDef.getParent().getParent(); |
280 | } | |
281 | ||
282 |
3
1. isInLatestScopeDefinition : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED 2. isInLatestScopeDefinition : replaced boolean return with false for com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::isInLatestScopeDefinition → KILLED 3. isInLatestScopeDefinition : replaced boolean return with true for com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::isInLatestScopeDefinition → KILLED |
return result; |
283 | } | |
284 | ||
285 | /** | |
286 | * Determine the visibility modifier and raise the corresponding counter. | |
287 | * | |
288 | * @param method | |
289 | * The method-subtree from the AbstractSyntaxTree. | |
290 | */ | |
291 | private void raiseCounter(DetailAST method) { | |
292 | final MethodCounter actualCounter = counters.peek(); | |
293 | final Scope scope = ScopeUtil.getScope(method); | |
294 |
1
1. raiseCounter : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck$MethodCounter::increment → KILLED |
actualCounter.increment(scope); |
295 | } | |
296 | ||
297 | /** | |
298 | * Check the counters and report violations. | |
299 | * | |
300 | * @param counter the method counters to check | |
301 | * @param ast to report violations against. | |
302 | */ | |
303 | private void checkCounters(MethodCounter counter, DetailAST ast) { | |
304 |
1
1. checkCounters : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::checkMax → KILLED |
checkMax(maxPrivate, counter.value(Scope.PRIVATE), |
305 | MSG_PRIVATE_METHODS, ast); | |
306 |
1
1. checkCounters : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::checkMax → KILLED |
checkMax(maxPackage, counter.value(Scope.PACKAGE), |
307 | MSG_PACKAGE_METHODS, ast); | |
308 |
1
1. checkCounters : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::checkMax → KILLED |
checkMax(maxProtected, counter.value(Scope.PROTECTED), |
309 | MSG_PROTECTED_METHODS, ast); | |
310 |
1
1. checkCounters : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::checkMax → KILLED |
checkMax(maxPublic, counter.value(Scope.PUBLIC), |
311 | MSG_PUBLIC_METHODS, ast); | |
312 |
1
1. checkCounters : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::checkMax → KILLED |
checkMax(maxTotal, counter.getTotal(), MSG_MANY_METHODS, ast); |
313 | } | |
314 | ||
315 | /** | |
316 | * Utility for reporting if a maximum has been exceeded. | |
317 | * | |
318 | * @param max the maximum allowed value | |
319 | * @param value the actual value | |
320 | * @param msg the message to log. Takes two arguments of value and maximum. | |
321 | * @param ast the AST to associate with the message. | |
322 | */ | |
323 | private void checkMax(int max, int value, String msg, DetailAST ast) { | |
324 |
4
1. checkMax : changed conditional boundary → KILLED 2. checkMax : negated conditional → KILLED 3. checkMax : removed conditional - replaced comparison check with false → KILLED 4. checkMax : removed conditional - replaced comparison check with true → KILLED |
if (max < value) { |
325 |
1
1. checkMax : removed call to com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck::log → KILLED |
log(ast, msg, value, max); |
326 | } | |
327 | } | |
328 | ||
329 | /** | |
330 | * Setter to specify the maximum number of {@code private} methods allowed. | |
331 | * | |
332 | * @param value the maximum allowed. | |
333 | */ | |
334 | public void setMaxPrivate(int value) { | |
335 | maxPrivate = value; | |
336 | } | |
337 | ||
338 | /** | |
339 | * Setter to specify the maximum number of {@code package} methods allowed. | |
340 | * | |
341 | * @param value the maximum allowed. | |
342 | */ | |
343 | public void setMaxPackage(int value) { | |
344 | maxPackage = value; | |
345 | } | |
346 | ||
347 | /** | |
348 | * Setter to specify the maximum number of {@code protected} methods allowed. | |
349 | * | |
350 | * @param value the maximum allowed. | |
351 | */ | |
352 | public void setMaxProtected(int value) { | |
353 | maxProtected = value; | |
354 | } | |
355 | ||
356 | /** | |
357 | * Setter to specify the maximum number of {@code public} methods allowed. | |
358 | * | |
359 | * @param value the maximum allowed. | |
360 | */ | |
361 | public void setMaxPublic(int value) { | |
362 | maxPublic = value; | |
363 | } | |
364 | ||
365 | /** | |
366 | * Setter to specify the maximum number of methods allowed at all scope levels. | |
367 | * | |
368 | * @param value the maximum allowed. | |
369 | */ | |
370 | public void setMaxTotal(int value) { | |
371 | maxTotal = value; | |
372 | } | |
373 | ||
374 | /** | |
375 | * Marker class used to collect data about the number of methods per | |
376 | * class. Objects of this class are used on the Stack to count the | |
377 | * methods for each class and layer. | |
378 | */ | |
379 | private static class MethodCounter { | |
380 | ||
381 | /** Maintains the counts. */ | |
382 |
1
1. <init> : removed call to java/util/EnumMap::<init> → KILLED |
private final Map<Scope, Integer> counts = new EnumMap<>(Scope.class); |
383 | /** | |
384 | * The surrounding scope definition (class, enum, etc.) which the method counts are | |
385 | * connected to. | |
386 | */ | |
387 | private final DetailAST scopeDefinition; | |
388 | /** Tracks the total. */ | |
389 | private int total; | |
390 | ||
391 | /** | |
392 | * Creates an interface. | |
393 | * | |
394 | * @param scopeDefinition | |
395 | * The surrounding scope definition (class, enum, etc.) which to count all methods | |
396 | * for. | |
397 | */ | |
398 | /* package */ MethodCounter(DetailAST scopeDefinition) { | |
399 | this.scopeDefinition = scopeDefinition; | |
400 | } | |
401 | ||
402 | /** | |
403 | * Increments to counter by one for the supplied scope. | |
404 | * | |
405 | * @param scope the scope counter to increment. | |
406 | */ | |
407 | private void increment(Scope scope) { | |
408 |
1
1. increment : Replaced integer addition with subtraction → KILLED |
total++; |
409 |
1
1. increment : Replaced integer addition with subtraction → KILLED |
counts.put(scope, 1 + value(scope)); |
410 | } | |
411 | ||
412 | /** | |
413 | * Gets the value of a scope counter. | |
414 | * | |
415 | * @param scope the scope counter to get the value of | |
416 | * @return the value of a scope counter | |
417 | */ | |
418 | private int value(Scope scope) { | |
419 | Integer value = counts.get(scope); | |
420 |
3
1. value : negated conditional → KILLED 2. value : removed conditional - replaced equality check with false → KILLED 3. value : removed conditional - replaced equality check with true → KILLED |
if (value == null) { |
421 | value = 0; | |
422 | } | |
423 |
1
1. value : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return value; |
424 | } | |
425 | ||
426 | /** | |
427 | * Returns the surrounding scope definition (class, enum, etc.) which the method counts | |
428 | * are connected to. | |
429 | * | |
430 | * @return the surrounding scope definition | |
431 | */ | |
432 | private DetailAST getScopeDefinition() { | |
433 |
1
1. getScopeDefinition : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/sizes/MethodCountCheck$MethodCounter::getScopeDefinition to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return scopeDefinition; |
434 | } | |
435 | ||
436 | /** | |
437 | * Fetches total number of methods. | |
438 | * | |
439 | * @return the total number of methods. | |
440 | */ | |
441 | private int getTotal() { | |
442 |
1
1. getTotal : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return total; |
443 | } | |
444 | ||
445 | } | |
446 | ||
447 | } | |
Mutations | ||
207 |
1.1 |
|
222 |
1.1 |
|
227 |
1.1 |
|
240 |
1.1 |
|
245 |
1.1 2.2 3.3 |
|
246 |
1.1 2.2 3.3 |
|
247 |
1.1 |
|
251 |
1.1 2.2 |
|
257 |
1.1 2.2 3.3 |
|
260 |
1.1 |
|
276 |
1.1 2.2 3.3 |
|
279 |
1.1 2.2 3.3 |
|
282 |
1.1 2.2 3.3 |
|
294 |
1.1 |
|
304 |
1.1 |
|
306 |
1.1 |
|
308 |
1.1 |
|
310 |
1.1 |
|
312 |
1.1 |
|
324 |
1.1 2.2 3.3 4.4 |
|
325 |
1.1 |
|
382 |
1.1 |
|
408 |
1.1 |
|
409 |
1.1 |
|
420 |
1.1 2.2 3.3 |
|
423 |
1.1 |
|
433 |
1.1 |
|
442 |
1.1 |