1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.FileStatefulCheck;
26 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27 import com.puppycrawl.tools.checkstyle.api.DetailAST;
28 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 @FileStatefulCheck
113 public class JavaNCSSCheck extends AbstractCheck {
114
115
116
117
118
119 public static final String MSG_METHOD = "ncss.method";
120
121
122
123
124
125 public static final String MSG_CLASS = "ncss.class";
126
127
128
129
130
131 public static final String MSG_RECORD = "ncss.record";
132
133
134
135
136
137 public static final String MSG_FILE = "ncss.file";
138
139
140 private static final int FILE_MAX_NCSS = 2000;
141
142
143 private static final int CLASS_MAX_NCSS = 1500;
144
145
146 private static final int RECORD_MAX_NCSS = 150;
147
148
149 private static final int METHOD_MAX_NCSS = 50;
150
151
152
153
154
155 private int fileMaximum = FILE_MAX_NCSS;
156
157
158 private int classMaximum = CLASS_MAX_NCSS;
159
160
161 private int recordMaximum = RECORD_MAX_NCSS;
162
163
164 private int methodMaximum = METHOD_MAX_NCSS;
165
166
167 private Deque<Counter> counters;
168
169 @Override
170 public int[] getDefaultTokens() {
171 return getRequiredTokens();
172 }
173
174 @Override
175 public int[] getRequiredTokens() {
176 return new int[] {
177 TokenTypes.CLASS_DEF,
178 TokenTypes.INTERFACE_DEF,
179 TokenTypes.METHOD_DEF,
180 TokenTypes.CTOR_DEF,
181 TokenTypes.INSTANCE_INIT,
182 TokenTypes.STATIC_INIT,
183 TokenTypes.PACKAGE_DEF,
184 TokenTypes.IMPORT,
185 TokenTypes.VARIABLE_DEF,
186 TokenTypes.CTOR_CALL,
187 TokenTypes.SUPER_CTOR_CALL,
188 TokenTypes.LITERAL_IF,
189 TokenTypes.LITERAL_ELSE,
190 TokenTypes.LITERAL_WHILE,
191 TokenTypes.LITERAL_DO,
192 TokenTypes.LITERAL_FOR,
193 TokenTypes.LITERAL_SWITCH,
194 TokenTypes.LITERAL_BREAK,
195 TokenTypes.LITERAL_CONTINUE,
196 TokenTypes.LITERAL_RETURN,
197 TokenTypes.LITERAL_THROW,
198 TokenTypes.LITERAL_SYNCHRONIZED,
199 TokenTypes.LITERAL_CATCH,
200 TokenTypes.LITERAL_FINALLY,
201 TokenTypes.EXPR,
202 TokenTypes.LABELED_STAT,
203 TokenTypes.LITERAL_CASE,
204 TokenTypes.LITERAL_DEFAULT,
205 TokenTypes.RECORD_DEF,
206 TokenTypes.COMPACT_CTOR_DEF,
207 };
208 }
209
210 @Override
211 public int[] getAcceptableTokens() {
212 return getRequiredTokens();
213 }
214
215 @Override
216 public void beginTree(DetailAST rootAST) {
217 counters = new ArrayDeque<>();
218
219
220 counters.push(new Counter());
221 }
222
223 @Override
224 public void visitToken(DetailAST ast) {
225 final int tokenType = ast.getType();
226
227 if (tokenType == TokenTypes.CLASS_DEF
228 || tokenType == TokenTypes.RECORD_DEF
229 || isMethodOrCtorOrInitDefinition(tokenType)) {
230
231 counters.push(new Counter());
232 }
233
234
235 if (isCountable(ast)) {
236
237 counters.forEach(Counter::increment);
238 }
239 }
240
241 @Override
242 public void leaveToken(DetailAST ast) {
243 final int tokenType = ast.getType();
244
245 if (isMethodOrCtorOrInitDefinition(tokenType)) {
246
247 final Counter counter = counters.pop();
248
249 final int count = counter.getCount();
250 if (count > methodMaximum) {
251 log(ast, MSG_METHOD, count, methodMaximum);
252 }
253 }
254 else if (tokenType == TokenTypes.CLASS_DEF) {
255
256 final Counter counter = counters.pop();
257
258 final int count = counter.getCount();
259 if (count > classMaximum) {
260 log(ast, MSG_CLASS, count, classMaximum);
261 }
262 }
263 else if (tokenType == TokenTypes.RECORD_DEF) {
264
265 final Counter counter = counters.pop();
266
267 final int count = counter.getCount();
268 if (count > recordMaximum) {
269 log(ast, MSG_RECORD, count, recordMaximum);
270 }
271 }
272 }
273
274 @Override
275 public void finishTree(DetailAST rootAST) {
276
277 final Counter counter = counters.pop();
278
279 final int count = counter.getCount();
280 if (count > fileMaximum) {
281 log(rootAST, MSG_FILE, count, fileMaximum);
282 }
283 }
284
285
286
287
288
289
290
291
292
293 public void setFileMaximum(int fileMaximum) {
294 this.fileMaximum = fileMaximum;
295 }
296
297
298
299
300
301
302
303
304 public void setClassMaximum(int classMaximum) {
305 this.classMaximum = classMaximum;
306 }
307
308
309
310
311
312
313
314
315 public void setRecordMaximum(int recordMaximum) {
316 this.recordMaximum = recordMaximum;
317 }
318
319
320
321
322
323
324
325
326 public void setMethodMaximum(int methodMaximum) {
327 this.methodMaximum = methodMaximum;
328 }
329
330
331
332
333
334
335
336
337 private static boolean isCountable(DetailAST ast) {
338 boolean countable = true;
339
340 final int tokenType = ast.getType();
341
342
343 if (tokenType == TokenTypes.EXPR) {
344 countable = isExpressionCountable(ast);
345 }
346
347 else if (tokenType == TokenTypes.VARIABLE_DEF) {
348 countable = isVariableDefCountable(ast);
349 }
350 return countable;
351 }
352
353
354
355
356
357
358
359 private static boolean isVariableDefCountable(DetailAST ast) {
360 boolean countable = false;
361
362
363
364 final int parentType = ast.getParent().getType();
365
366 if (parentType == TokenTypes.SLIST
367 || parentType == TokenTypes.OBJBLOCK) {
368 final DetailAST prevSibling = ast.getPreviousSibling();
369
370
371
372
373
374 countable = prevSibling == null
375 || prevSibling.getType() != TokenTypes.COMMA;
376 }
377
378 return countable;
379 }
380
381
382
383
384
385
386
387 private static boolean isExpressionCountable(DetailAST ast) {
388 final boolean countable;
389
390
391
392
393 final int parentType = ast.getParent().getType();
394 switch (parentType) {
395 case TokenTypes.SLIST:
396 case TokenTypes.LABELED_STAT:
397 case TokenTypes.LITERAL_FOR:
398 case TokenTypes.LITERAL_DO:
399 case TokenTypes.LITERAL_WHILE:
400 case TokenTypes.LITERAL_IF:
401 case TokenTypes.LITERAL_ELSE:
402
403 final DetailAST prevSibling = ast.getPreviousSibling();
404 countable = prevSibling == null
405 || prevSibling.getType() != TokenTypes.LPAREN;
406 break;
407 default:
408 countable = false;
409 break;
410 }
411 return countable;
412 }
413
414
415
416
417
418
419
420 private static boolean isMethodOrCtorOrInitDefinition(int tokenType) {
421 return tokenType == TokenTypes.METHOD_DEF
422 || tokenType == TokenTypes.COMPACT_CTOR_DEF
423 || tokenType == TokenTypes.CTOR_DEF
424 || tokenType == TokenTypes.STATIC_INIT
425 || tokenType == TokenTypes.INSTANCE_INIT;
426 }
427
428
429
430
431
432 private static final class Counter {
433
434
435 private int count;
436
437
438
439
440 public void increment() {
441 count++;
442 }
443
444
445
446
447
448
449 public int getCount() {
450 return count;
451 }
452
453 }
454
455 }