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.ArrayList;
23 import java.util.List;
24
25 import com.puppycrawl.tools.checkstyle.StatelessCheck;
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 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
49
50
51 @StatelessCheck
52 public class OpenjdkAnnotationLocationCheck extends AbstractCheck {
53
54
55
56
57
58 public static final String MSG_KEY_ANNOTATION_ALONE_OR_SAME = "annotation.alone.or.same";
59
60
61
62
63
64 public static final String MSG_KEY_ANNOTATION_ON_TARGET_LINE = "annotation.on.target.line";
65
66
67
68
69 public OpenjdkAnnotationLocationCheck() {
70
71 }
72
73 @Override
74 public int[] getDefaultTokens() {
75 return getAcceptableTokens();
76 }
77
78 @Override
79 public int[] getAcceptableTokens() {
80 return new int[] {
81 TokenTypes.CLASS_DEF,
82 TokenTypes.INTERFACE_DEF,
83 TokenTypes.PACKAGE_DEF,
84 TokenTypes.ENUM_CONSTANT_DEF,
85 TokenTypes.ENUM_DEF,
86 TokenTypes.METHOD_DEF,
87 TokenTypes.CTOR_DEF,
88 TokenTypes.VARIABLE_DEF,
89 TokenTypes.ANNOTATION_DEF,
90 TokenTypes.ANNOTATION_FIELD_DEF,
91 TokenTypes.RECORD_DEF,
92 TokenTypes.COMPACT_CTOR_DEF,
93 TokenTypes.MODULE_DEF,
94 };
95 }
96
97 @Override
98 public int[] getRequiredTokens() {
99 return CommonUtil.EMPTY_INT_ARRAY;
100 }
101
102 @Override
103 public void visitToken(DetailAST ast) {
104 final DetailAST annotationParentNode = getAnnotationsNode(ast);
105 final DetailAST startOfTargetNode = getStartingAst(annotationParentNode);
106 final List<DetailAST> annotationList = getAnnotations(annotationParentNode);
107
108 final boolean areAnnotationsOnSameLine = areAllOnSameLine(annotationList);
109 if (!areAnnotationsOnSameLine && !areAllOnSeparateLines(annotationList)) {
110 log(startOfTargetNode, MSG_KEY_ANNOTATION_ALONE_OR_SAME, getTargetName(ast));
111 }
112 if (isAnyOnTargetLine(annotationList, startOfTargetNode)
113 && !(areAnnotationsOnSameLine
114 && isSingleLineTarget(startOfTargetNode, ast))) {
115 log(startOfTargetNode, MSG_KEY_ANNOTATION_ON_TARGET_LINE, getTargetName(ast));
116 }
117 }
118
119
120
121
122
123
124
125 private static DetailAST getStartingAst(DetailAST targetNode) {
126 DetailAST annotation = targetNode.getFirstChild();
127 while (annotation != null && annotation.getType() == TokenTypes.ANNOTATION) {
128 annotation = annotation.getNextSibling();
129 }
130
131 final DetailAST startingAst;
132 if (annotation != null) {
133 startingAst = annotation;
134 }
135 else {
136 startingAst = targetNode.getNextSibling();
137 }
138 return startingAst;
139 }
140
141
142
143
144
145
146
147 private static DetailAST getAnnotationsNode(DetailAST ast) {
148 DetailAST annotationParentNode = ast.findFirstToken(TokenTypes.MODIFIERS);
149 if (annotationParentNode == null) {
150 annotationParentNode = ast.findFirstToken(TokenTypes.ANNOTATIONS);
151 }
152 return annotationParentNode;
153 }
154
155
156
157
158
159
160
161 private static List<DetailAST> getAnnotations(DetailAST annotationParentNode) {
162 final List<DetailAST> annotationList = new ArrayList<>();
163 DetailAST annotation = annotationParentNode.getFirstChild();
164 while (annotation != null && annotation.getType() == TokenTypes.ANNOTATION) {
165 annotationList.add(annotation);
166 annotation = annotation.getNextSibling();
167 }
168 return annotationList;
169 }
170
171
172
173
174
175
176
177 private static boolean areAllOnSameLine(List<DetailAST> annotationList) {
178 return annotationList.isEmpty()
179 || annotationList.getFirst().getLineNo() == annotationList.getLast().getLineNo();
180 }
181
182
183
184
185
186
187
188 private static boolean areAllOnSeparateLines(List<DetailAST> annotationList) {
189 boolean areOnSeparateLine = true;
190 for (int index = 0; index < annotationList.size() - 1; index++) {
191 if (annotationList.get(index).getLineNo()
192 == annotationList.get(index + 1).getLineNo()) {
193 areOnSeparateLine = false;
194 }
195 }
196 return areOnSeparateLine;
197 }
198
199
200
201
202
203
204
205
206 private static boolean isAnyOnTargetLine(Iterable<DetailAST> annotationList,
207 DetailAST startOfTargetNode) {
208 boolean isOnTargetLine = false;
209 for (final DetailAST annotation : annotationList) {
210 if (TokenUtil.areOnSameLine(annotation, startOfTargetNode)) {
211 isOnTargetLine = true;
212 }
213 }
214 return isOnTargetLine;
215 }
216
217
218
219
220
221
222
223
224 private static boolean isSingleLineTarget(DetailAST startOfTargetNode,
225 DetailAST targetNode) {
226 DetailAST lastToken = targetNode;
227 while (lastToken.hasChildren()) {
228 lastToken = lastToken.getLastChild();
229 }
230 return TokenUtil.areOnSameLine(startOfTargetNode, lastToken);
231 }
232
233
234
235
236
237
238
239 private static String getTargetName(DetailAST targetNode) {
240 DetailAST identNode = targetNode.findFirstToken(TokenTypes.IDENT);
241 if (identNode == null) {
242 identNode = targetNode.findFirstToken(TokenTypes.DOT).findFirstToken(TokenTypes.IDENT);
243 }
244 return identNode.getText();
245 }
246
247 }