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 @StatelessCheck
40 public class WhitespaceAfterCheck
41 extends AbstractCheck {
42
43
44
45
46
47 public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
48
49
50
51
52
53 public static final String MSG_WS_TYPECAST = "ws.typeCast";
54
55
56
57
58 public WhitespaceAfterCheck() {
59
60 }
61
62 @Override
63 public int[] getDefaultTokens() {
64 return new int[] {
65 TokenTypes.COMMA,
66 TokenTypes.SEMI,
67 TokenTypes.TYPECAST,
68 TokenTypes.LITERAL_IF,
69 TokenTypes.LITERAL_ELSE,
70 TokenTypes.LITERAL_WHILE,
71 TokenTypes.LITERAL_DO,
72 TokenTypes.LITERAL_FOR,
73 TokenTypes.LITERAL_FINALLY,
74 TokenTypes.LITERAL_RETURN,
75 TokenTypes.LITERAL_YIELD,
76 TokenTypes.LITERAL_CATCH,
77 TokenTypes.DO_WHILE,
78 TokenTypes.ELLIPSIS,
79 TokenTypes.LITERAL_SWITCH,
80 TokenTypes.LITERAL_SYNCHRONIZED,
81 TokenTypes.LITERAL_TRY,
82 TokenTypes.LITERAL_CASE,
83 TokenTypes.LAMBDA,
84 TokenTypes.LITERAL_WHEN,
85 };
86 }
87
88 @Override
89 public int[] getAcceptableTokens() {
90 return new int[] {
91 TokenTypes.COMMA,
92 TokenTypes.SEMI,
93 TokenTypes.TYPECAST,
94 TokenTypes.LITERAL_IF,
95 TokenTypes.LITERAL_ELSE,
96 TokenTypes.LITERAL_WHILE,
97 TokenTypes.LITERAL_DO,
98 TokenTypes.LITERAL_FOR,
99 TokenTypes.LITERAL_FINALLY,
100 TokenTypes.LITERAL_RETURN,
101 TokenTypes.LITERAL_YIELD,
102 TokenTypes.LITERAL_CATCH,
103 TokenTypes.DO_WHILE,
104 TokenTypes.ELLIPSIS,
105 TokenTypes.LITERAL_SWITCH,
106 TokenTypes.LITERAL_SYNCHRONIZED,
107 TokenTypes.LITERAL_TRY,
108 TokenTypes.LITERAL_CASE,
109 TokenTypes.LAMBDA,
110 TokenTypes.LITERAL_WHEN,
111 TokenTypes.ANNOTATIONS,
112 TokenTypes.SINGLE_LINE_COMMENT,
113 };
114 }
115
116 @Override
117 public int[] getRequiredTokens() {
118 return CommonUtil.EMPTY_INT_ARRAY;
119 }
120
121 @Override
122 public boolean isCommentNodesRequired() {
123 return true;
124 }
125
126 @Override
127 public void visitToken(DetailAST ast) {
128 final int type = ast.getType();
129 if (type == TokenTypes.TYPECAST) {
130 final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN);
131 final int[] line = getLineCodePoints(targetAST.getLineNo() - 1);
132 if (!isFollowedByWhitespace(targetAST, line)) {
133 log(targetAST, MSG_WS_TYPECAST);
134 }
135 }
136 else if (type == TokenTypes.ANNOTATIONS) {
137 if (ast.getFirstChild() != null) {
138 DetailAST targetAST = ast.getFirstChild().getLastChild();
139 if (targetAST.getType() == TokenTypes.DOT) {
140 targetAST = targetAST.getLastChild();
141 }
142 final int[] line = getLineCodePoints(targetAST.getLineNo() - 1);
143 if (!isFollowedByWhitespace(targetAST, line)) {
144 final Object[] message = {targetAST.getText()};
145 log(targetAST, MSG_WS_NOT_FOLLOWED, message);
146 }
147 }
148 }
149 else if (type == TokenTypes.SINGLE_LINE_COMMENT) {
150 if (!isCommentFollowedByWhitespace(ast)) {
151 log(ast, MSG_WS_NOT_FOLLOWED, ast.getText());
152 }
153 }
154 else {
155 final int[] line = getLineCodePoints(ast.getLineNo() - 1);
156 if (!isFollowedByWhitespace(ast, line)) {
157 final Object[] message = {ast.getText()};
158 log(ast, MSG_WS_NOT_FOLLOWED, message);
159 }
160 }
161 }
162
163
164
165
166
167
168
169
170 private static boolean isCommentFollowedByWhitespace(DetailAST ast) {
171 final DetailAST commentContent = ast.findFirstToken(TokenTypes.COMMENT_CONTENT);
172 final String commentText = commentContent.getText();
173 int index = 0;
174 while (commentText.charAt(index) == '/') {
175 index++;
176 }
177 final int codePoint = commentText.codePointAt(index);
178 return Character.isWhitespace(codePoint);
179 }
180
181
182
183
184
185
186
187
188 private static boolean isFollowedByWhitespace(DetailAST targetAST, int... line) {
189 final int after =
190 targetAST.getColumnNo() + targetAST.getText().length();
191 boolean followedByWhitespace = true;
192
193 if (after < line.length) {
194 final int codePoint = line[after];
195
196 followedByWhitespace = codePoint == ';'
197 || codePoint == ')'
198 || Character.isWhitespace(codePoint);
199 }
200 return followedByWhitespace;
201 }
202
203 }