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.whitespace;
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
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 @StatelessCheck
83 public class NoWhitespaceAfterCheck extends AbstractCheck {
84
85
86
87
88
89 public static final String MSG_KEY = "ws.followed";
90
91
92 private boolean allowLineBreaks = true;
93
94 @Override
95 public int[] getDefaultTokens() {
96 return new int[] {
97 TokenTypes.ARRAY_INIT,
98 TokenTypes.AT,
99 TokenTypes.INC,
100 TokenTypes.DEC,
101 TokenTypes.UNARY_MINUS,
102 TokenTypes.UNARY_PLUS,
103 TokenTypes.BNOT,
104 TokenTypes.LNOT,
105 TokenTypes.DOT,
106 TokenTypes.ARRAY_DECLARATOR,
107 TokenTypes.INDEX_OP,
108 };
109 }
110
111 @Override
112 public int[] getAcceptableTokens() {
113 return new int[] {
114 TokenTypes.ARRAY_INIT,
115 TokenTypes.AT,
116 TokenTypes.INC,
117 TokenTypes.DEC,
118 TokenTypes.UNARY_MINUS,
119 TokenTypes.UNARY_PLUS,
120 TokenTypes.BNOT,
121 TokenTypes.LNOT,
122 TokenTypes.DOT,
123 TokenTypes.TYPECAST,
124 TokenTypes.ARRAY_DECLARATOR,
125 TokenTypes.INDEX_OP,
126 TokenTypes.LITERAL_SYNCHRONIZED,
127 TokenTypes.METHOD_REF,
128 };
129 }
130
131 @Override
132 public int[] getRequiredTokens() {
133 return CommonUtil.EMPTY_INT_ARRAY;
134 }
135
136
137
138
139
140
141 public void setAllowLineBreaks(boolean allowLineBreaks) {
142 this.allowLineBreaks = allowLineBreaks;
143 }
144
145 @Override
146 public void visitToken(DetailAST ast) {
147 final DetailAST whitespaceFollowedAst = getWhitespaceFollowedNode(ast);
148
149 if (whitespaceFollowedAst.getNextSibling() == null
150 || whitespaceFollowedAst.getNextSibling().getType() != TokenTypes.ANNOTATIONS) {
151 final int whitespaceColumnNo = getPositionAfter(whitespaceFollowedAst);
152 final int whitespaceLineNo = whitespaceFollowedAst.getLineNo();
153
154 if (hasTrailingWhitespace(ast, whitespaceColumnNo, whitespaceLineNo)) {
155 log(ast, MSG_KEY, whitespaceFollowedAst.getText());
156 }
157 }
158 }
159
160
161
162
163
164
165
166
167 private static DetailAST getWhitespaceFollowedNode(DetailAST ast) {
168 final DetailAST whitespaceFollowedAst;
169 switch (ast.getType()) {
170 case TokenTypes.TYPECAST:
171 whitespaceFollowedAst = ast.findFirstToken(TokenTypes.RPAREN);
172 break;
173 case TokenTypes.ARRAY_DECLARATOR:
174 whitespaceFollowedAst = getArrayDeclaratorPreviousElement(ast);
175 break;
176 case TokenTypes.INDEX_OP:
177 whitespaceFollowedAst = getIndexOpPreviousElement(ast);
178 break;
179 default:
180 whitespaceFollowedAst = ast;
181 }
182 return whitespaceFollowedAst;
183 }
184
185
186
187
188
189
190 private static int getPositionAfter(DetailAST ast) {
191 final int after;
192
193 if (ast.getType() == TokenTypes.IDENT
194 && ast.getNextSibling() != null
195 && ast.getNextSibling().getType() == TokenTypes.LPAREN) {
196 final DetailAST methodDef = ast.getParent();
197 final DetailAST endOfParams = methodDef.findFirstToken(TokenTypes.RPAREN);
198 after = endOfParams.getColumnNo() + 1;
199 }
200 else {
201 after = ast.getColumnNo() + ast.getText().length();
202 }
203 return after;
204 }
205
206
207
208
209
210
211
212
213
214
215
216 private boolean hasTrailingWhitespace(DetailAST ast,
217 int whitespaceColumnNo, int whitespaceLineNo) {
218 final boolean result;
219 final int astLineNo = ast.getLineNo();
220 final String line = getLine(astLineNo - 1);
221 if (astLineNo == whitespaceLineNo && whitespaceColumnNo < line.length()) {
222 result = Character.isWhitespace(line.charAt(whitespaceColumnNo));
223 }
224 else {
225 result = !allowLineBreaks;
226 }
227 return result;
228 }
229
230
231
232
233
234
235
236
237
238 private static DetailAST getArrayDeclaratorPreviousElement(DetailAST ast) {
239 final DetailAST previousElement;
240 final DetailAST firstChild = ast.getFirstChild();
241 if (firstChild.getType() == TokenTypes.ARRAY_DECLARATOR) {
242
243 previousElement = firstChild.findFirstToken(TokenTypes.RBRACK);
244 }
245 else {
246
247 final DetailAST parent = getFirstNonArrayDeclaratorParent(ast);
248 switch (parent.getType()) {
249
250 case TokenTypes.TYPE_ARGUMENT:
251 final DetailAST wildcard = parent.findFirstToken(TokenTypes.WILDCARD_TYPE);
252 if (wildcard == null) {
253
254 previousElement = getTypeLastNode(ast);
255 }
256 else {
257
258 previousElement = getTypeLastNode(ast.getFirstChild());
259 }
260 break;
261
262 case TokenTypes.LITERAL_NEW:
263 previousElement = getTypeLastNode(parent);
264 break;
265
266 case TokenTypes.TYPE:
267 previousElement = getPreviousNodeWithParentOfTypeAst(ast, parent);
268 break;
269
270 case TokenTypes.DOT:
271 previousElement = getTypeLastNode(ast);
272 break;
273
274 case TokenTypes.METHOD_REF:
275 final DetailAST ident = getIdentLastToken(ast);
276 if (ident == null) {
277
278 previousElement = ast.getFirstChild();
279 }
280 else {
281 previousElement = ident;
282 }
283 break;
284 default:
285 throw new IllegalStateException("unexpected ast syntax " + parent);
286 }
287 }
288 return previousElement;
289 }
290
291
292
293
294
295
296
297
298
299 private static DetailAST getIndexOpPreviousElement(DetailAST ast) {
300 final DetailAST result;
301 final DetailAST firstChild = ast.getFirstChild();
302 if (firstChild.getType() == TokenTypes.INDEX_OP) {
303
304 result = firstChild.findFirstToken(TokenTypes.RBRACK);
305 }
306 else {
307 final DetailAST ident = getIdentLastToken(ast);
308 if (ident == null) {
309 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
310
311 if (rparen == null) {
312 final DetailAST lastChild = firstChild.getLastChild();
313 result = lastChild.findFirstToken(TokenTypes.RCURLY);
314 }
315
316 else {
317 result = rparen;
318 }
319 }
320 else {
321 result = ident;
322 }
323 }
324 return result;
325 }
326
327
328
329
330
331
332
333 private static DetailAST getFirstNonArrayDeclaratorParent(DetailAST ast) {
334 DetailAST parent = ast.getParent();
335 while (parent.getType() == TokenTypes.ARRAY_DECLARATOR) {
336 parent = parent.getParent();
337 }
338 return parent;
339 }
340
341
342
343
344
345
346
347
348 private static DetailAST getTypeLastNode(DetailAST ast) {
349 DetailAST result = ast.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
350 if (result == null) {
351 result = getIdentLastToken(ast);
352 if (result == null) {
353
354 result = ast.getFirstChild();
355 }
356 }
357 else {
358 result = result.findFirstToken(TokenTypes.GENERIC_END);
359 }
360 return result;
361 }
362
363
364
365
366
367
368
369
370
371
372 private static DetailAST getPreviousNodeWithParentOfTypeAst(DetailAST ast, DetailAST parent) {
373 final DetailAST previousElement;
374 final DetailAST ident = getIdentLastToken(parent.getParent());
375 final DetailAST lastTypeNode = getTypeLastNode(ast);
376
377
378
379
380 if (ident == null || ident.getLineNo() > ast.getLineNo()) {
381 previousElement = lastTypeNode;
382 }
383 else if (ident.getLineNo() < ast.getLineNo()) {
384 previousElement = ident;
385 }
386
387 else {
388 final int instanceOfSize = 13;
389
390 if (ident.getColumnNo() >= ast.getColumnNo() + 2
391
392
393 || lastTypeNode.getColumnNo() >= ident.getColumnNo() + instanceOfSize) {
394 previousElement = lastTypeNode;
395 }
396 else {
397 previousElement = ident;
398 }
399 }
400 return previousElement;
401 }
402
403
404
405
406
407
408
409 private static DetailAST getIdentLastToken(DetailAST ast) {
410 final DetailAST result;
411 final DetailAST dot = ast.findFirstToken(TokenTypes.DOT);
412
413 if (dot == null) {
414 final DetailAST methodCall = ast.findFirstToken(TokenTypes.METHOD_CALL);
415 if (methodCall == null) {
416 result = ast.findFirstToken(TokenTypes.IDENT);
417 }
418 else {
419 result = methodCall.findFirstToken(TokenTypes.RPAREN);
420 }
421 }
422
423 else {
424 result = dot.getFirstChild().getNextSibling();
425 }
426 return result;
427 }
428
429 }