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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
27 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
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 @StatelessCheck
76 public class AnnotationLocationCheck extends AbstractCheck {
77
78
79
80
81
82 public static final String MSG_KEY_ANNOTATION_LOCATION_ALONE = "annotation.location.alone";
83
84
85
86
87
88 public static final String MSG_KEY_ANNOTATION_LOCATION = "annotation.location";
89
90
91
92
93
94 private boolean allowSamelineSingleParameterlessAnnotation = true;
95
96
97
98
99
100 private boolean allowSamelineParameterizedAnnotation;
101
102
103
104
105
106 private boolean allowSamelineMultipleAnnotations;
107
108
109
110
111
112
113
114
115 public final void setAllowSamelineSingleParameterlessAnnotation(boolean allow) {
116 allowSamelineSingleParameterlessAnnotation = allow;
117 }
118
119
120
121
122
123
124
125
126 public final void setAllowSamelineParameterizedAnnotation(boolean allow) {
127 allowSamelineParameterizedAnnotation = allow;
128 }
129
130
131
132
133
134
135
136
137 public final void setAllowSamelineMultipleAnnotations(boolean allow) {
138 allowSamelineMultipleAnnotations = allow;
139 }
140
141 @Override
142 public int[] getDefaultTokens() {
143 return new int[] {
144 TokenTypes.CLASS_DEF,
145 TokenTypes.INTERFACE_DEF,
146 TokenTypes.PACKAGE_DEF,
147 TokenTypes.ENUM_CONSTANT_DEF,
148 TokenTypes.ENUM_DEF,
149 TokenTypes.METHOD_DEF,
150 TokenTypes.CTOR_DEF,
151 TokenTypes.VARIABLE_DEF,
152 TokenTypes.RECORD_DEF,
153 TokenTypes.COMPACT_CTOR_DEF,
154 };
155 }
156
157 @Override
158 public int[] getAcceptableTokens() {
159 return new int[] {
160 TokenTypes.CLASS_DEF,
161 TokenTypes.INTERFACE_DEF,
162 TokenTypes.PACKAGE_DEF,
163 TokenTypes.ENUM_CONSTANT_DEF,
164 TokenTypes.ENUM_DEF,
165 TokenTypes.METHOD_DEF,
166 TokenTypes.CTOR_DEF,
167 TokenTypes.VARIABLE_DEF,
168 TokenTypes.ANNOTATION_DEF,
169 TokenTypes.ANNOTATION_FIELD_DEF,
170 TokenTypes.RECORD_DEF,
171 TokenTypes.COMPACT_CTOR_DEF,
172 };
173 }
174
175 @Override
176 public int[] getRequiredTokens() {
177 return CommonUtil.EMPTY_INT_ARRAY;
178 }
179
180 @Override
181 public void visitToken(DetailAST ast) {
182
183 if (ast.getType() != TokenTypes.VARIABLE_DEF
184 || ast.getParent().getType() == TokenTypes.OBJBLOCK) {
185 DetailAST node = ast.findFirstToken(TokenTypes.MODIFIERS);
186 if (node == null) {
187 node = ast.findFirstToken(TokenTypes.ANNOTATIONS);
188 }
189 checkAnnotations(node, getExpectedAnnotationIndentation(node));
190 }
191 }
192
193
194
195
196
197
198
199
200 private static int getExpectedAnnotationIndentation(DetailAST node) {
201 return node.getColumnNo();
202 }
203
204
205
206
207
208
209
210
211
212 private void checkAnnotations(DetailAST modifierNode, int correctIndentation) {
213 DetailAST annotation = modifierNode.getFirstChild();
214
215 while (annotation != null && annotation.getType() == TokenTypes.ANNOTATION) {
216 final boolean hasParameters = isParameterized(annotation);
217
218 if (!isCorrectLocation(annotation, hasParameters)) {
219 log(annotation,
220 MSG_KEY_ANNOTATION_LOCATION_ALONE, getAnnotationName(annotation));
221 }
222 else if (annotation.getColumnNo() != correctIndentation && !hasNodeBefore(annotation)) {
223 log(annotation, MSG_KEY_ANNOTATION_LOCATION,
224 getAnnotationName(annotation), annotation.getColumnNo(), correctIndentation);
225 }
226 annotation = annotation.getNextSibling();
227 }
228 }
229
230
231
232
233
234
235
236 private static boolean isParameterized(DetailAST annotation) {
237 return TokenUtil.findFirstTokenByPredicate(annotation, ast -> {
238 return ast.getType() == TokenTypes.EXPR
239 || ast.getType() == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR;
240 }).isPresent();
241 }
242
243
244
245
246
247
248
249 private static String getAnnotationName(DetailAST annotation) {
250 DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT);
251 if (identNode == null) {
252 identNode = annotation.findFirstToken(TokenTypes.DOT).findFirstToken(TokenTypes.IDENT);
253 }
254 return identNode.getText();
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 private boolean isCorrectLocation(DetailAST annotation, boolean hasParams) {
273 final boolean allowingCondition;
274
275 if (hasParams) {
276 allowingCondition = allowSamelineParameterizedAnnotation;
277 }
278 else {
279 allowingCondition = allowSamelineSingleParameterlessAnnotation;
280 }
281 return allowSamelineMultipleAnnotations
282 || allowingCondition && !hasNodeBefore(annotation)
283 || !hasNodeBeside(annotation);
284 }
285
286
287
288
289
290
291
292 private static boolean hasNodeBefore(DetailAST annotation) {
293 final int annotationLineNo = annotation.getLineNo();
294 final DetailAST previousNode = annotation.getPreviousSibling();
295
296 return previousNode != null && annotationLineNo == previousNode.getLineNo();
297 }
298
299
300
301
302
303
304
305 private static boolean hasNodeBeside(DetailAST annotation) {
306 return hasNodeBefore(annotation) || hasNodeAfter(annotation);
307 }
308
309
310
311
312
313
314
315 private static boolean hasNodeAfter(DetailAST annotation) {
316 final int annotationLineNo = annotation.getLineNo();
317 DetailAST nextNode = annotation.getNextSibling();
318
319 if (nextNode == null) {
320 nextNode = annotation.getParent().getNextSibling();
321 }
322
323 return annotationLineNo == nextNode.getLineNo();
324 }
325
326 }