Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
WhitespaceAroundCheck |
|
| 5.75;5.75 |
1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2014 Oliver Burn | |
4 | // | |
5 | // This library is free software; you can redistribute it and/or | |
6 | // modify it under the terms of the GNU Lesser General Public | |
7 | // License as published by the Free Software Foundation; either | |
8 | // version 2.1 of the License, or (at your option) any later version. | |
9 | // | |
10 | // This library is distributed in the hope that it will be useful, | |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | // Lesser General Public License for more details. | |
14 | // | |
15 | // You should have received a copy of the GNU Lesser General Public | |
16 | // License along with this library; if not, write to the Free Software | |
17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | //////////////////////////////////////////////////////////////////////////////// | |
19 | package com.puppycrawl.tools.checkstyle.checks.whitespace; | |
20 | ||
21 | import com.puppycrawl.tools.checkstyle.api.Check; | |
22 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
23 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
24 | ||
25 | /** | |
26 | * <p> | |
27 | * Checks that a token is surrounded by whitespace. | |
28 | * </p> | |
29 | * <p> By default the check will check the following operators: | |
30 | * {@link TokenTypes#LITERAL_ASSERT ASSERT}, | |
31 | * {@link TokenTypes#ASSIGN ASSIGN}, | |
32 | * {@link TokenTypes#BAND BAND}, | |
33 | * {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN}, | |
34 | * {@link TokenTypes#BOR BOR}, | |
35 | * {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN}, | |
36 | * {@link TokenTypes#BSR BSR}, | |
37 | * {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN}, | |
38 | * {@link TokenTypes#BXOR BXOR}, | |
39 | * {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN}, | |
40 | * {@link TokenTypes#COLON COLON}, | |
41 | * {@link TokenTypes#DIV DIV}, | |
42 | * {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN}, | |
43 | * {@link TokenTypes#EQUAL EQUAL}, | |
44 | * {@link TokenTypes#GE GE}, | |
45 | * {@link TokenTypes#GT GT}, | |
46 | * {@link TokenTypes#LAND LAND}, | |
47 | * {@link TokenTypes#LCURLY LCURLY}, | |
48 | * {@link TokenTypes#LE LE}, | |
49 | * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, | |
50 | * {@link TokenTypes#LITERAL_DO LITERAL_DO}, | |
51 | * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}, | |
52 | * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY}, | |
53 | * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}, | |
54 | * {@link TokenTypes#LITERAL_IF LITERAL_IF}, | |
55 | * {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN}, | |
56 | * {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED}, | |
57 | * {@link TokenTypes#LITERAL_TRY LITERAL_TRY}, | |
58 | * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}, | |
59 | * {@link TokenTypes#LOR LOR}, | |
60 | * {@link TokenTypes#LT LT}, | |
61 | * {@link TokenTypes#MINUS MINUS}, | |
62 | * {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN}, | |
63 | * {@link TokenTypes#MOD MOD}, | |
64 | * {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN}, | |
65 | * {@link TokenTypes#NOT_EQUAL NOT_EQUAL}, | |
66 | * {@link TokenTypes#PLUS PLUS}, | |
67 | * {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN}, | |
68 | * {@link TokenTypes#QUESTION QUESTION}, | |
69 | * {@link TokenTypes#RCURLY RCURLY}, | |
70 | * {@link TokenTypes#SL SL}, | |
71 | * {@link TokenTypes#SLIST SLIST}, | |
72 | * {@link TokenTypes#SL_ASSIGN SL_ASSIGN}, | |
73 | * {@link TokenTypes#SR SR}, | |
74 | * {@link TokenTypes#SR_ASSIGN SR_ASSIGN}, | |
75 | * {@link TokenTypes#STAR STAR}, | |
76 | * {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}. | |
77 | * {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT}. | |
78 | * {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}. | |
79 | * </p> | |
80 | * <p> | |
81 | * An example of how to configure the check is: | |
82 | * </p> | |
83 | * <pre> | |
84 | * <module name="WhitespaceAround"/> | |
85 | * </pre> | |
86 | * <p> An example of how to configure the check for whitespace only around | |
87 | * assignment operators is: | |
88 | * </p> | |
89 | * <pre> | |
90 | * <module name="WhitespaceAround"> | |
91 | * <property name="tokens" | |
92 | * value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/> | |
93 | * </module> | |
94 | * </pre> | |
95 | * <p> | |
96 | * In addition, this check can be configured to allow empty method and/or | |
97 | * constructor bodies. For example, a method with an empty body might look | |
98 | * like: | |
99 | * </p> | |
100 | * <p> | |
101 | * <pre> public void doSomething(int val) {}</pre> | |
102 | * </p> | |
103 | * <p> | |
104 | * To configure the check to allow empty method blocks use | |
105 | * </p> | |
106 | * <p> | |
107 | * <pre> <property name="allowEmptyMethods" value="true" /></pre> | |
108 | * </p> | |
109 | * <p> | |
110 | * To configure the check to allow empty constructor blocks use | |
111 | * </p> | |
112 | * <p> | |
113 | * <pre> <property name="allowEmptyConstructors" value="true" /></pre> | |
114 | * </p> | |
115 | * | |
116 | * <p> | |
117 | * Also, this check can be configured to ignore the colon in an enhanced for | |
118 | * loop. The colon in an enhanced for loop is ignored by default | |
119 | * </p> | |
120 | * <p> | |
121 | * To configure the check to ignore the colon | |
122 | * </p> | |
123 | * <p> | |
124 | * <pre> <property name="ignoreEnhancedForColon" value="true" /></pre> | |
125 | * </p> | |
126 | * | |
127 | * @author Oliver Burn | |
128 | * @version 1.0 | |
129 | */ | |
130 | 7 | public class WhitespaceAroundCheck extends Check |
131 | { | |
132 | /** Whether or not empty constructor bodies are allowed. */ | |
133 | private boolean mAllowEmptyCtors; | |
134 | /** Whether or not empty method bodies are allowed. */ | |
135 | private boolean mAllowEmptyMethods; | |
136 | /** whether or not to ignore a colon in a enhanced for loop */ | |
137 | 7 | private boolean mIgnoreEnhancedForColon = true; |
138 | ||
139 | @Override | |
140 | public int[] getDefaultTokens() | |
141 | { | |
142 | 7 | return new int[] { |
143 | TokenTypes.ASSIGN, | |
144 | TokenTypes.BAND, | |
145 | TokenTypes.BAND_ASSIGN, | |
146 | TokenTypes.BOR, | |
147 | TokenTypes.BOR_ASSIGN, | |
148 | TokenTypes.BSR, | |
149 | TokenTypes.BSR_ASSIGN, | |
150 | TokenTypes.BXOR, | |
151 | TokenTypes.BXOR_ASSIGN, | |
152 | TokenTypes.COLON, | |
153 | TokenTypes.DIV, | |
154 | TokenTypes.DIV_ASSIGN, | |
155 | TokenTypes.EQUAL, | |
156 | TokenTypes.GE, | |
157 | TokenTypes.GT, | |
158 | TokenTypes.LAND, | |
159 | TokenTypes.LCURLY, | |
160 | TokenTypes.LE, | |
161 | TokenTypes.LITERAL_CATCH, | |
162 | TokenTypes.LITERAL_DO, | |
163 | TokenTypes.LITERAL_ELSE, | |
164 | TokenTypes.LITERAL_FINALLY, | |
165 | TokenTypes.LITERAL_FOR, | |
166 | TokenTypes.LITERAL_IF, | |
167 | TokenTypes.LITERAL_RETURN, | |
168 | TokenTypes.LITERAL_SYNCHRONIZED, | |
169 | TokenTypes.LITERAL_TRY, | |
170 | TokenTypes.LITERAL_WHILE, | |
171 | TokenTypes.LOR, | |
172 | TokenTypes.LT, | |
173 | TokenTypes.MINUS, | |
174 | TokenTypes.MINUS_ASSIGN, | |
175 | TokenTypes.MOD, | |
176 | TokenTypes.MOD_ASSIGN, | |
177 | TokenTypes.NOT_EQUAL, | |
178 | TokenTypes.PLUS, | |
179 | TokenTypes.PLUS_ASSIGN, | |
180 | TokenTypes.QUESTION, | |
181 | TokenTypes.RCURLY, | |
182 | TokenTypes.SL, | |
183 | TokenTypes.SLIST, | |
184 | TokenTypes.SL_ASSIGN, | |
185 | TokenTypes.SR, | |
186 | TokenTypes.SR_ASSIGN, | |
187 | TokenTypes.STAR, | |
188 | TokenTypes.STAR_ASSIGN, | |
189 | TokenTypes.LITERAL_ASSERT, | |
190 | TokenTypes.TYPE_EXTENSION_AND, | |
191 | }; | |
192 | } | |
193 | ||
194 | /** | |
195 | * Sets whether or not empty method bodies are allowed. | |
196 | * @param aAllow <code>true</code> to allow empty method bodies. | |
197 | */ | |
198 | public void setAllowEmptyMethods(boolean aAllow) | |
199 | { | |
200 | 1 | mAllowEmptyMethods = aAllow; |
201 | 1 | } |
202 | ||
203 | /** | |
204 | * Sets whether or not empty constructor bodies are allowed. | |
205 | * @param aAllow <code>true</code> to allow empty constructor bodies. | |
206 | */ | |
207 | public void setAllowEmptyConstructors(boolean aAllow) | |
208 | { | |
209 | 1 | mAllowEmptyCtors = aAllow; |
210 | 1 | } |
211 | ||
212 | /** | |
213 | * Sets whether or not to ignore the whitespace around the | |
214 | * colon in an enhanced for loop. | |
215 | * @param aIgnore <code>true</code> to ignore enhanced for colon. | |
216 | */ | |
217 | public void setIgnoreEnhancedForColon(boolean aIgnore) | |
218 | { | |
219 | 1 | mIgnoreEnhancedForColon = aIgnore; |
220 | 1 | } |
221 | ||
222 | @Override | |
223 | public void visitToken(DetailAST aAST) | |
224 | { | |
225 | 412 | final int type = aAST.getType(); |
226 | 412 | final int parentType = aAST.getParent().getType(); |
227 | ||
228 | // Check for CURLY in array initializer | |
229 | 412 | if (((type == TokenTypes.RCURLY) || (type == TokenTypes.LCURLY)) |
230 | && (parentType == TokenTypes.ARRAY_INIT)) | |
231 | { | |
232 | 1 | return; |
233 | } | |
234 | ||
235 | // Check for import pkg.name.*; | |
236 | 411 | if ((type == TokenTypes.STAR) |
237 | && (parentType == TokenTypes.DOT)) | |
238 | { | |
239 | 1 | return; |
240 | } | |
241 | ||
242 | // Check for an SLIST that has a parent CASE_GROUP. It is not a '{'. | |
243 | 410 | if ((type == TokenTypes.SLIST) |
244 | && (parentType == TokenTypes.CASE_GROUP)) | |
245 | { | |
246 | 0 | return; |
247 | } | |
248 | ||
249 | 410 | if ((type == TokenTypes.COLON)) { |
250 | //we do not want to check colon for cases and defaults | |
251 | 10 | if (parentType == TokenTypes.LITERAL_DEFAULT |
252 | || parentType == TokenTypes.LITERAL_CASE) | |
253 | { | |
254 | 0 | return; |
255 | } | |
256 | 10 | else if (parentType == TokenTypes.FOR_EACH_CLAUSE |
257 | && this.mIgnoreEnhancedForColon) | |
258 | { | |
259 | 3 | return; |
260 | } | |
261 | } | |
262 | ||
263 | // Check for allowed empty method or ctor blocks. | |
264 | 407 | if (emptyMethodBlockCheck(aAST, parentType) |
265 | || emptyCtorBlockCheck(aAST, parentType)) | |
266 | { | |
267 | 10 | return; |
268 | } | |
269 | ||
270 | 397 | final String[] lines = getLines(); |
271 | 397 | final String line = lines[aAST.getLineNo() - 1]; |
272 | 397 | final int before = aAST.getColumnNo() - 1; |
273 | 397 | final int after = aAST.getColumnNo() + aAST.getText().length(); |
274 | ||
275 | 397 | if ((before >= 0) && !Character.isWhitespace(line.charAt(before))) { |
276 | 19 | log(aAST.getLineNo(), aAST.getColumnNo(), |
277 | "ws.notPreceded", aAST.getText()); | |
278 | } | |
279 | ||
280 | 397 | if (after >= line.length()) { |
281 | 198 | return; |
282 | } | |
283 | ||
284 | 199 | final char nextChar = line.charAt(after); |
285 | 199 | if (!Character.isWhitespace(nextChar) |
286 | // Check for "return;" | |
287 | && !((type == TokenTypes.LITERAL_RETURN) | |
288 | && (aAST.getFirstChild().getType() == TokenTypes.SEMI)) | |
289 | // Check for "})" or "};" or "},". Happens with anon-inners | |
290 | && !((type == TokenTypes.RCURLY) | |
291 | && ((nextChar == ')') | |
292 | || (nextChar == ';') | |
293 | || (nextChar == ',')))) | |
294 | { | |
295 | 33 | log(aAST.getLineNo(), aAST.getColumnNo() + aAST.getText().length(), |
296 | "ws.notFollowed", aAST.getText()); | |
297 | } | |
298 | 199 | } |
299 | ||
300 | /** | |
301 | * Test if the given <code>DetailAST</code> is part of an allowed empty | |
302 | * method block. | |
303 | * @param aAST the <code>DetailAST</code> to test. | |
304 | * @param aParentType the token type of <code>aAST</code>'s parent. | |
305 | * @return <code>true</code> if <code>aAST</code> makes up part of an | |
306 | * allowed empty method block. | |
307 | */ | |
308 | private boolean emptyMethodBlockCheck(DetailAST aAST, int aParentType) | |
309 | { | |
310 | 407 | return mAllowEmptyMethods |
311 | && emptyBlockCheck(aAST, aParentType, TokenTypes.METHOD_DEF); | |
312 | } | |
313 | ||
314 | /** | |
315 | * Test if the given <code>DetailAST</code> is part of an allowed empty | |
316 | * constructor (ctor) block. | |
317 | * @param aAST the <code>DetailAST</code> to test. | |
318 | * @param aParentType the token type of <code>aAST</code>'s parent. | |
319 | * @return <code>true</code> if <code>aAST</code> makes up part of an | |
320 | * allowed empty constructor block. | |
321 | */ | |
322 | private boolean emptyCtorBlockCheck(DetailAST aAST, int aParentType) | |
323 | { | |
324 | 399 | return mAllowEmptyCtors |
325 | && emptyBlockCheck(aAST, aParentType, TokenTypes.CTOR_DEF); | |
326 | } | |
327 | ||
328 | /** | |
329 | * Test if the given <code>DetailAST</code> is part of an empty block. | |
330 | * An example empty block might look like the following | |
331 | * <p> | |
332 | * <pre> public void myMethod(int val) {}</pre> | |
333 | * <p> | |
334 | * In the above, the method body is an empty block ("{}"). | |
335 | * | |
336 | * @param aAST the <code>DetailAST</code> to test. | |
337 | * @param aParentType the token type of <code>aAST</code>'s parent. | |
338 | * @param aMatch the parent token type we're looking to match. | |
339 | * @return <code>true</code> if <code>aAST</code> makes up part of an | |
340 | * empty block contained under a <code>aMatch</code> token type | |
341 | * node. | |
342 | */ | |
343 | private boolean emptyBlockCheck(DetailAST aAST, int aParentType, int aMatch) | |
344 | { | |
345 | 142 | final int type = aAST.getType(); |
346 | 142 | if (type == TokenTypes.RCURLY) { |
347 | 29 | final DetailAST grandParent = aAST.getParent().getParent(); |
348 | 29 | return (aParentType == TokenTypes.SLIST) |
349 | && (grandParent.getType() == aMatch); | |
350 | } | |
351 | ||
352 | 113 | return (type == TokenTypes.SLIST) |
353 | && (aParentType == aMatch) | |
354 | && (aAST.getFirstChild().getType() == TokenTypes.RCURLY); | |
355 | } | |
356 | } |