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;
21
22 import java.util.Arrays;
23
24 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
25 import com.puppycrawl.tools.checkstyle.PropertyType;
26 import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
27 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28 import com.puppycrawl.tools.checkstyle.api.DetailAST;
29 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 @FileStatefulCheck
49 public class DescendantTokenCheck extends AbstractCheck {
50
51
52
53
54
55 public static final String MSG_KEY_MIN = "descendant.token.min";
56
57
58
59
60
61 public static final String MSG_KEY_MAX = "descendant.token.max";
62
63
64
65
66
67 public static final String MSG_KEY_SUM_MIN = "descendant.token.sum.min";
68
69
70
71
72
73 public static final String MSG_KEY_SUM_MAX = "descendant.token.sum.max";
74
75
76 private int minimumDepth;
77
78 private int maximumDepth = Integer.MAX_VALUE;
79
80 private int minimumNumber;
81
82 private int maximumNumber = Integer.MAX_VALUE;
83
84
85
86
87 private boolean sumTokenCounts;
88
89 @XdocsPropertyType(PropertyType.TOKEN_ARRAY)
90 private int[] limitedTokens = CommonUtil.EMPTY_INT_ARRAY;
91
92 private String minimumMessage;
93
94 private String maximumMessage;
95
96
97
98
99
100 private int[] counts = CommonUtil.EMPTY_INT_ARRAY;
101
102 @Override
103 public int[] getAcceptableTokens() {
104 return TokenUtil.getAllTokenIds();
105 }
106
107 @Override
108 public int[] getDefaultTokens() {
109 return getRequiredTokens();
110 }
111
112 @Override
113 public int[] getRequiredTokens() {
114 return CommonUtil.EMPTY_INT_ARRAY;
115 }
116
117 @Override
118 public void visitToken(DetailAST ast) {
119
120 Arrays.fill(counts, 0);
121 countTokens(ast, 0);
122
123 if (sumTokenCounts) {
124 logAsTotal(ast);
125 }
126 else {
127 logAsSeparated(ast);
128 }
129 }
130
131
132
133
134
135
136 private void logAsSeparated(DetailAST ast) {
137
138 final String name = TokenUtil.getTokenName(ast.getType());
139
140 for (int element : limitedTokens) {
141 final int tokenCount = counts[element - 1];
142 if (tokenCount < minimumNumber) {
143 final String descendantName = TokenUtil.getTokenName(element);
144
145 if (minimumMessage == null) {
146 minimumMessage = MSG_KEY_MIN;
147 }
148 log(ast,
149 minimumMessage,
150 String.valueOf(tokenCount),
151 String.valueOf(minimumNumber),
152 name,
153 descendantName);
154 }
155 if (tokenCount > maximumNumber) {
156 final String descendantName = TokenUtil.getTokenName(element);
157
158 if (maximumMessage == null) {
159 maximumMessage = MSG_KEY_MAX;
160 }
161 log(ast,
162 maximumMessage,
163 String.valueOf(tokenCount),
164 String.valueOf(maximumNumber),
165 name,
166 descendantName);
167 }
168 }
169 }
170
171
172
173
174
175
176 private void logAsTotal(DetailAST ast) {
177
178 final String name = TokenUtil.getTokenName(ast.getType());
179
180 int total = 0;
181 for (int element : limitedTokens) {
182 total += counts[element - 1];
183 }
184 if (total < minimumNumber) {
185 if (minimumMessage == null) {
186 minimumMessage = MSG_KEY_SUM_MIN;
187 }
188 log(ast,
189 minimumMessage,
190 String.valueOf(total),
191 String.valueOf(minimumNumber), name);
192 }
193 if (total > maximumNumber) {
194 if (maximumMessage == null) {
195 maximumMessage = MSG_KEY_SUM_MAX;
196 }
197 log(ast,
198 maximumMessage,
199 String.valueOf(total),
200 String.valueOf(maximumNumber), name);
201 }
202 }
203
204
205
206
207
208
209
210 private void countTokens(DetailAST ast, int depth) {
211 if (depth <= maximumDepth) {
212
213 if (depth >= minimumDepth) {
214 final int type = ast.getType();
215 if (type <= counts.length) {
216 counts[type - 1]++;
217 }
218 }
219 DetailAST child = ast.getFirstChild();
220 final int nextDepth = depth + 1;
221 while (child != null) {
222 countTokens(child, nextDepth);
223 child = child.getNextSibling();
224 }
225 }
226 }
227
228
229
230
231
232
233
234 public void setLimitedTokens(String... limitedTokensParam) {
235 limitedTokens = new int[limitedTokensParam.length];
236
237 int maxToken = 0;
238 for (int i = 0; i < limitedTokensParam.length; i++) {
239 limitedTokens[i] = TokenUtil.getTokenId(limitedTokensParam[i]);
240 if (limitedTokens[i] >= maxToken + 1) {
241 maxToken = limitedTokens[i];
242 }
243 }
244 counts = new int[maxToken];
245 }
246
247
248
249
250
251
252
253 public void setMinimumDepth(int minimumDepth) {
254 this.minimumDepth = minimumDepth;
255 }
256
257
258
259
260
261
262
263 public void setMaximumDepth(int maximumDepth) {
264 this.maximumDepth = maximumDepth;
265 }
266
267
268
269
270
271
272
273 public void setMinimumNumber(int minimumNumber) {
274 this.minimumNumber = minimumNumber;
275 }
276
277
278
279
280
281
282
283 public void setMaximumNumber(int maximumNumber) {
284 this.maximumNumber = maximumNumber;
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300 public void setMinimumMessage(String message) {
301 minimumMessage = message;
302 }
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318 public void setMaximumMessage(String message) {
319 maximumMessage = message;
320 }
321
322
323
324
325
326
327
328
329 public void setSumTokenCounts(boolean sum) {
330 sumTokenCounts = sum;
331 }
332
333 }