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