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.annotation;
21
22 import java.util.Locale;
23
24 import com.puppycrawl.tools.checkstyle.StatelessCheck;
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
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 @StatelessCheck
113 public final class AnnotationUseStyleCheck extends AbstractCheck {
114
115
116
117
118 public enum ElementStyleOption {
119
120
121
122
123
124
125 EXPANDED,
126
127
128
129
130
131
132
133
134 COMPACT,
135
136
137
138
139
140
141 COMPACT_NO_ARRAY,
142
143
144
145
146 IGNORE,
147
148 }
149
150
151
152
153
154
155 public enum TrailingArrayCommaOption {
156
157
158
159
160
161
162 ALWAYS,
163
164
165
166
167
168
169 NEVER,
170
171
172
173
174 IGNORE,
175
176 }
177
178
179
180
181
182
183 public enum ClosingParensOption {
184
185
186
187
188
189
190 ALWAYS,
191
192
193
194
195
196
197 NEVER,
198
199
200
201
202 IGNORE,
203
204 }
205
206
207
208
209
210 public static final String MSG_KEY_ANNOTATION_INCORRECT_STYLE =
211 "annotation.incorrect.style";
212
213
214
215
216
217 public static final String MSG_KEY_ANNOTATION_PARENS_MISSING =
218 "annotation.parens.missing";
219
220
221
222
223
224 public static final String MSG_KEY_ANNOTATION_PARENS_PRESENT =
225 "annotation.parens.present";
226
227
228
229
230
231 public static final String MSG_KEY_ANNOTATION_TRAILING_COMMA_MISSING =
232 "annotation.trailing.comma.missing";
233
234
235
236
237
238 public static final String MSG_KEY_ANNOTATION_TRAILING_COMMA_PRESENT =
239 "annotation.trailing.comma.present";
240
241
242
243
244
245 private static final String ANNOTATION_ELEMENT_SINGLE_NAME =
246 "value";
247
248
249
250
251 private ElementStyleOption elementStyle = ElementStyleOption.COMPACT_NO_ARRAY;
252
253
254
255
256
257 private TrailingArrayCommaOption trailingArrayComma = TrailingArrayCommaOption.NEVER;
258
259
260
261
262 private ClosingParensOption closingParens = ClosingParensOption.NEVER;
263
264
265
266
267
268
269
270 public void setElementStyle(final String style) {
271 elementStyle = getOption(ElementStyleOption.class, style);
272 }
273
274
275
276
277
278
279
280 public void setTrailingArrayComma(final String comma) {
281 trailingArrayComma = getOption(TrailingArrayCommaOption.class, comma);
282 }
283
284
285
286
287
288
289
290 public void setClosingParens(final String parens) {
291 closingParens = getOption(ClosingParensOption.class, parens);
292 }
293
294
295
296
297
298
299
300
301
302
303 private static <T extends Enum<T>> T getOption(final Class<T> enumClass,
304 final String value) {
305 try {
306 return Enum.valueOf(enumClass, value.trim().toUpperCase(Locale.ENGLISH));
307 }
308 catch (final IllegalArgumentException iae) {
309 throw new IllegalArgumentException("unable to parse " + value, iae);
310 }
311 }
312
313 @Override
314 public int[] getDefaultTokens() {
315 return getRequiredTokens();
316 }
317
318 @Override
319 public int[] getRequiredTokens() {
320 return new int[] {
321 TokenTypes.ANNOTATION,
322 };
323 }
324
325 @Override
326 public int[] getAcceptableTokens() {
327 return getRequiredTokens();
328 }
329
330 @Override
331 public void visitToken(final DetailAST ast) {
332 checkStyleType(ast);
333 checkCheckClosingParensOption(ast);
334 checkTrailingComma(ast);
335 }
336
337
338
339
340
341
342
343
344
345
346 private void checkStyleType(final DetailAST annotation) {
347 switch (elementStyle) {
348 case COMPACT_NO_ARRAY:
349 checkCompactNoArrayStyle(annotation);
350 break;
351 case COMPACT:
352 checkCompactStyle(annotation);
353 break;
354 case EXPANDED:
355 checkExpandedStyle(annotation);
356 break;
357 case IGNORE:
358 default:
359 break;
360 }
361 }
362
363
364
365
366
367
368 private void checkExpandedStyle(final DetailAST annotation) {
369 final int valuePairCount =
370 annotation.getChildCount(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
371
372 if (valuePairCount == 0 && hasArguments(annotation)) {
373 log(annotation, MSG_KEY_ANNOTATION_INCORRECT_STYLE, ElementStyleOption.EXPANDED);
374 }
375 }
376
377
378
379
380
381
382
383 private static boolean hasArguments(DetailAST annotation) {
384 final DetailAST firstToken = annotation.findFirstToken(TokenTypes.LPAREN);
385 return firstToken != null && firstToken.getNextSibling().getType() != TokenTypes.RPAREN;
386 }
387
388
389
390
391
392
393 private void checkCompactStyle(final DetailAST annotation) {
394 final int valuePairCount =
395 annotation.getChildCount(
396 TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
397
398 final DetailAST valuePair =
399 annotation.findFirstToken(
400 TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
401
402 if (valuePairCount == 1
403 && ANNOTATION_ELEMENT_SINGLE_NAME.equals(
404 valuePair.getFirstChild().getText())) {
405 log(annotation, MSG_KEY_ANNOTATION_INCORRECT_STYLE,
406 ElementStyleOption.COMPACT);
407 }
408 }
409
410
411
412
413
414
415 private void checkCompactNoArrayStyle(final DetailAST annotation) {
416 final DetailAST arrayInit =
417 annotation.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT);
418
419
420 if (arrayInit != null
421 && arrayInit.getChildCount(TokenTypes.EXPR) == 1) {
422 log(annotation, MSG_KEY_ANNOTATION_INCORRECT_STYLE,
423 ElementStyleOption.COMPACT_NO_ARRAY);
424 }
425
426 else {
427 DetailAST ast = annotation.getFirstChild();
428 while (ast != null) {
429 final DetailAST nestedArrayInit =
430 ast.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT);
431 if (nestedArrayInit != null
432 && nestedArrayInit.getChildCount(TokenTypes.EXPR) == 1) {
433 log(annotation, MSG_KEY_ANNOTATION_INCORRECT_STYLE,
434 ElementStyleOption.COMPACT_NO_ARRAY);
435 }
436 ast = ast.getNextSibling();
437 }
438 }
439 }
440
441
442
443
444
445
446
447 private void checkTrailingComma(final DetailAST annotation) {
448 if (trailingArrayComma != TrailingArrayCommaOption.IGNORE) {
449 DetailAST child = annotation.getFirstChild();
450
451 while (child != null) {
452 DetailAST arrayInit = null;
453
454 if (child.getType() == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
455 arrayInit = child.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT);
456 }
457 else if (child.getType() == TokenTypes.ANNOTATION_ARRAY_INIT) {
458 arrayInit = child;
459 }
460
461 if (arrayInit != null) {
462 logCommaViolation(arrayInit);
463 }
464 child = child.getNextSibling();
465 }
466 }
467 }
468
469
470
471
472
473
474
475 private void logCommaViolation(final DetailAST ast) {
476 final DetailAST rCurly = ast.findFirstToken(TokenTypes.RCURLY);
477
478
479 final DetailAST comma = rCurly.getPreviousSibling();
480
481 if (trailingArrayComma == TrailingArrayCommaOption.NEVER) {
482 if (comma != null && comma.getType() == TokenTypes.COMMA) {
483 log(comma, MSG_KEY_ANNOTATION_TRAILING_COMMA_PRESENT);
484 }
485 }
486 else if (comma == null || comma.getType() != TokenTypes.COMMA) {
487 log(rCurly, MSG_KEY_ANNOTATION_TRAILING_COMMA_MISSING);
488 }
489 }
490
491
492
493
494
495
496
497 private void checkCheckClosingParensOption(final DetailAST ast) {
498 if (closingParens != ClosingParensOption.IGNORE) {
499 final DetailAST paren = ast.getLastChild();
500
501 if (closingParens == ClosingParensOption.NEVER) {
502 if (paren.getPreviousSibling().getType() == TokenTypes.LPAREN) {
503 log(ast, MSG_KEY_ANNOTATION_PARENS_PRESENT);
504 }
505 }
506 else if (paren.getType() != TokenTypes.RPAREN) {
507 log(ast, MSG_KEY_ANNOTATION_PARENS_MISSING);
508 }
509 }
510 }
511
512 }