001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.whitespace;
021
022import com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027
028/**
029 * <div>
030 * Checks that a token is surrounded by whitespace. Empty constructor,
031 * method, class, enum, interface, loop bodies (blocks), lambdas of the form
032 * </div>
033 * <pre>
034 * public MyClass() {}      // empty constructor
035 * public void func() {}    // empty method
036 * public interface Foo {} // empty interface
037 * public class Foo {} // empty class
038 * public enum Foo {} // empty enum
039 * MyClass c = new MyClass() {}; // empty anonymous class
040 * while (i = 1) {} // empty while loop
041 * for (int i = 1; i &gt; 1; i++) {} // empty for loop
042 * do {} while (i = 1); // empty do-while loop
043 * Runnable noop = () -&gt; {}; // empty lambda
044 * public @interface Beta {} // empty annotation type
045 * </pre>
046 *
047 * <p>
048 * may optionally be exempted from the policy using the {@code allowEmptyMethods},
049 * {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops},
050 * {@code allowEmptyLambdas}, {@code allowEmptyCatches}
051 * and {@code allowEmptySwitchBlockStatements} properties.
052 * </p>
053 *
054 * <p>
055 * This check does not flag as violation double brace initialization like:
056 * </p>
057 * <pre>
058 * new Properties() {{
059 *     setProperty("key", "value");
060 * }};
061 * </pre>
062 *
063 * <p>
064 * Parameter allowEmptyCatches allows to suppress violations when token list
065 * contains SLIST to check if beginning of block is surrounded by whitespace
066 * and catch block is empty, for example:
067 * </p>
068 * <pre>
069 * try {
070 *     k = 5 / i;
071 * } catch (ArithmeticException ex) {}
072 * </pre>
073 *
074 * <p>
075 * With this property turned off, this raises violation because the beginning
076 * of the catch block (left curly bracket) is not separated from the end
077 * of the catch block (right curly bracket).
078 * </p>
079 *
080 * <p>
081 * Note: <a href="https://openjdk.org/jeps/361">
082 * Switch expressions</a> are ignored by this check.
083 * </p>
084 * <ul>
085 * <li>
086 * Property {@code allowEmptyCatches} - Allow empty catch bodies.
087 * Type is {@code boolean}.
088 * Default value is {@code false}.
089 * </li>
090 * <li>
091 * Property {@code allowEmptyConstructors} - Allow empty constructor bodies.
092 * Type is {@code boolean}.
093 * Default value is {@code false}.
094 * </li>
095 * <li>
096 * Property {@code allowEmptyLambdas} - Allow empty lambda bodies.
097 * Type is {@code boolean}.
098 * Default value is {@code false}.
099 * </li>
100 * <li>
101 * Property {@code allowEmptyLoops} - Allow empty loop bodies.
102 * Type is {@code boolean}.
103 * Default value is {@code false}.
104 * </li>
105 * <li>
106 * Property {@code allowEmptyMethods} - Allow empty method bodies.
107 * Type is {@code boolean}.
108 * Default value is {@code false}.
109 * </li>
110 * <li>
111 * Property {@code allowEmptySwitchBlockStatements} - Allow empty switch blocks
112 * and block statements.
113 * Type is {@code boolean}.
114 * Default value is {@code false}.
115 * </li>
116 * <li>
117 * Property {@code allowEmptyTypes} - Allow empty class, interface and enum bodies.
118 * Type is {@code boolean}.
119 * Default value is {@code false}.
120 * </li>
121 * <li>
122 * Property {@code ignoreEnhancedForColon} - Ignore whitespace around colon in
123 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
124 * enhanced for</a> loop.
125 * Type is {@code boolean}.
126 * Default value is {@code true}.
127 * </li>
128 * <li>
129 * Property {@code tokens} - tokens to check
130 * Type is {@code java.lang.String[]}.
131 * Validation type is {@code tokenSet}.
132 * Default value is:
133 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ASSIGN">
134 * ASSIGN</a>,
135 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND">
136 * BAND</a>,
137 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BAND_ASSIGN">
138 * BAND_ASSIGN</a>,
139 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR">
140 * BOR</a>,
141 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BOR_ASSIGN">
142 * BOR_ASSIGN</a>,
143 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR">
144 * BSR</a>,
145 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BSR_ASSIGN">
146 * BSR_ASSIGN</a>,
147 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR">
148 * BXOR</a>,
149 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#BXOR_ASSIGN">
150 * BXOR_ASSIGN</a>,
151 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COLON">
152 * COLON</a>,
153 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV">
154 * DIV</a>,
155 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DIV_ASSIGN">
156 * DIV_ASSIGN</a>,
157 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DO_WHILE">
158 * DO_WHILE</a>,
159 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#EQUAL">
160 * EQUAL</a>,
161 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GE">
162 * GE</a>,
163 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#GT">
164 * GT</a>,
165 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
166 * LAMBDA</a>,
167 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAND">
168 * LAND</a>,
169 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LCURLY">
170 * LCURLY</a>,
171 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LE">
172 * LE</a>,
173 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
174 * LITERAL_CATCH</a>,
175 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
176 * LITERAL_DO</a>,
177 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
178 * LITERAL_ELSE</a>,
179 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
180 * LITERAL_FINALLY</a>,
181 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
182 * LITERAL_FOR</a>,
183 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
184 * LITERAL_IF</a>,
185 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_RETURN">
186 * LITERAL_RETURN</a>,
187 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
188 * LITERAL_SWITCH</a>,
189 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
190 * LITERAL_SYNCHRONIZED</a>,
191 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
192 * LITERAL_TRY</a>,
193 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
194 * LITERAL_WHILE</a>,
195 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LOR">
196 * LOR</a>,
197 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LT">
198 * LT</a>,
199 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS">
200 * MINUS</a>,
201 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MINUS_ASSIGN">
202 * MINUS_ASSIGN</a>,
203 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD">
204 * MOD</a>,
205 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#MOD_ASSIGN">
206 * MOD_ASSIGN</a>,
207 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#NOT_EQUAL">
208 * NOT_EQUAL</a>,
209 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS">
210 * PLUS</a>,
211 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PLUS_ASSIGN">
212 * PLUS_ASSIGN</a>,
213 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#QUESTION">
214 * QUESTION</a>,
215 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RCURLY">
216 * RCURLY</a>,
217 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL">
218 * SL</a>,
219 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SLIST">
220 * SLIST</a>,
221 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SL_ASSIGN">
222 * SL_ASSIGN</a>,
223 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR">
224 * SR</a>,
225 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SR_ASSIGN">
226 * SR_ASSIGN</a>,
227 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR">
228 * STAR</a>,
229 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STAR_ASSIGN">
230 * STAR_ASSIGN</a>,
231 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ASSERT">
232 * LITERAL_ASSERT</a>,
233 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPE_EXTENSION_AND">
234 * TYPE_EXTENSION_AND</a>,
235 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHEN">
236 * LITERAL_WHEN</a>.
237 * </li>
238 * </ul>
239 *
240 * <p>
241 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
242 * </p>
243 *
244 * <p>
245 * Violation Message Keys:
246 * </p>
247 * <ul>
248 * <li>
249 * {@code ws.notFollowed}
250 * </li>
251 * <li>
252 * {@code ws.notPreceded}
253 * </li>
254 * </ul>
255 *
256 * @since 3.0
257 */
258@StatelessCheck
259public class WhitespaceAroundCheck extends AbstractCheck {
260
261    /**
262     * A key is pointing to the warning message text in "messages.properties"
263     * file.
264     */
265    public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
266
267    /**
268     * A key is pointing to the warning message text in "messages.properties"
269     * file.
270     */
271    public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
272
273    /** Allow empty constructor bodies. */
274    private boolean allowEmptyConstructors;
275    /** Allow empty method bodies. */
276    private boolean allowEmptyMethods;
277    /** Allow empty class, interface and enum bodies. */
278    private boolean allowEmptyTypes;
279    /** Allow empty loop bodies. */
280    private boolean allowEmptyLoops;
281    /** Allow empty lambda bodies. */
282    private boolean allowEmptyLambdas;
283    /** Allow empty catch bodies. */
284    private boolean allowEmptyCatches;
285    /** Allow empty switch blocks and block statements. */
286    private boolean allowEmptySwitchBlockStatements;
287    /**
288     * Ignore whitespace around colon in
289     * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
290     * enhanced for</a> loop.
291     */
292    private boolean ignoreEnhancedForColon = true;
293
294    @Override
295    public int[] getDefaultTokens() {
296        return new int[] {
297            TokenTypes.ASSIGN,
298            TokenTypes.BAND,
299            TokenTypes.BAND_ASSIGN,
300            TokenTypes.BOR,
301            TokenTypes.BOR_ASSIGN,
302            TokenTypes.BSR,
303            TokenTypes.BSR_ASSIGN,
304            TokenTypes.BXOR,
305            TokenTypes.BXOR_ASSIGN,
306            TokenTypes.COLON,
307            TokenTypes.DIV,
308            TokenTypes.DIV_ASSIGN,
309            TokenTypes.DO_WHILE,
310            TokenTypes.EQUAL,
311            TokenTypes.GE,
312            TokenTypes.GT,
313            TokenTypes.LAMBDA,
314            TokenTypes.LAND,
315            TokenTypes.LCURLY,
316            TokenTypes.LE,
317            TokenTypes.LITERAL_CATCH,
318            TokenTypes.LITERAL_DO,
319            TokenTypes.LITERAL_ELSE,
320            TokenTypes.LITERAL_FINALLY,
321            TokenTypes.LITERAL_FOR,
322            TokenTypes.LITERAL_IF,
323            TokenTypes.LITERAL_RETURN,
324            TokenTypes.LITERAL_SWITCH,
325            TokenTypes.LITERAL_SYNCHRONIZED,
326            TokenTypes.LITERAL_TRY,
327            TokenTypes.LITERAL_WHILE,
328            TokenTypes.LOR,
329            TokenTypes.LT,
330            TokenTypes.MINUS,
331            TokenTypes.MINUS_ASSIGN,
332            TokenTypes.MOD,
333            TokenTypes.MOD_ASSIGN,
334            TokenTypes.NOT_EQUAL,
335            TokenTypes.PLUS,
336            TokenTypes.PLUS_ASSIGN,
337            TokenTypes.QUESTION,
338            TokenTypes.RCURLY,
339            TokenTypes.SL,
340            TokenTypes.SLIST,
341            TokenTypes.SL_ASSIGN,
342            TokenTypes.SR,
343            TokenTypes.SR_ASSIGN,
344            TokenTypes.STAR,
345            TokenTypes.STAR_ASSIGN,
346            TokenTypes.LITERAL_ASSERT,
347            TokenTypes.TYPE_EXTENSION_AND,
348            TokenTypes.LITERAL_WHEN,
349        };
350    }
351
352    @Override
353    public int[] getAcceptableTokens() {
354        return new int[] {
355            TokenTypes.ASSIGN,
356            TokenTypes.ARRAY_INIT,
357            TokenTypes.BAND,
358            TokenTypes.BAND_ASSIGN,
359            TokenTypes.BOR,
360            TokenTypes.BOR_ASSIGN,
361            TokenTypes.BSR,
362            TokenTypes.BSR_ASSIGN,
363            TokenTypes.BXOR,
364            TokenTypes.BXOR_ASSIGN,
365            TokenTypes.COLON,
366            TokenTypes.DIV,
367            TokenTypes.DIV_ASSIGN,
368            TokenTypes.DO_WHILE,
369            TokenTypes.EQUAL,
370            TokenTypes.GE,
371            TokenTypes.GT,
372            TokenTypes.LAMBDA,
373            TokenTypes.LAND,
374            TokenTypes.LCURLY,
375            TokenTypes.LE,
376            TokenTypes.LITERAL_CATCH,
377            TokenTypes.LITERAL_DO,
378            TokenTypes.LITERAL_ELSE,
379            TokenTypes.LITERAL_FINALLY,
380            TokenTypes.LITERAL_FOR,
381            TokenTypes.LITERAL_IF,
382            TokenTypes.LITERAL_RETURN,
383            TokenTypes.LITERAL_SWITCH,
384            TokenTypes.LITERAL_SYNCHRONIZED,
385            TokenTypes.LITERAL_TRY,
386            TokenTypes.LITERAL_WHILE,
387            TokenTypes.LOR,
388            TokenTypes.LT,
389            TokenTypes.MINUS,
390            TokenTypes.MINUS_ASSIGN,
391            TokenTypes.MOD,
392            TokenTypes.MOD_ASSIGN,
393            TokenTypes.NOT_EQUAL,
394            TokenTypes.PLUS,
395            TokenTypes.PLUS_ASSIGN,
396            TokenTypes.QUESTION,
397            TokenTypes.RCURLY,
398            TokenTypes.SL,
399            TokenTypes.SLIST,
400            TokenTypes.SL_ASSIGN,
401            TokenTypes.SR,
402            TokenTypes.SR_ASSIGN,
403            TokenTypes.STAR,
404            TokenTypes.STAR_ASSIGN,
405            TokenTypes.LITERAL_ASSERT,
406            TokenTypes.TYPE_EXTENSION_AND,
407            TokenTypes.WILDCARD_TYPE,
408            TokenTypes.GENERIC_START,
409            TokenTypes.GENERIC_END,
410            TokenTypes.ELLIPSIS,
411            TokenTypes.LITERAL_WHEN,
412        };
413    }
414
415    @Override
416    public int[] getRequiredTokens() {
417        return CommonUtil.EMPTY_INT_ARRAY;
418    }
419
420    /**
421     * Setter to allow empty method bodies.
422     *
423     * @param allow {@code true} to allow empty method bodies.
424     * @since 4.0
425     */
426    public void setAllowEmptyMethods(boolean allow) {
427        allowEmptyMethods = allow;
428    }
429
430    /**
431     * Setter to allow empty constructor bodies.
432     *
433     * @param allow {@code true} to allow empty constructor bodies.
434     * @since 4.0
435     */
436    public void setAllowEmptyConstructors(boolean allow) {
437        allowEmptyConstructors = allow;
438    }
439
440    /**
441     * Setter to ignore whitespace around colon in
442     * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
443     * enhanced for</a> loop.
444     *
445     * @param ignore {@code true} to ignore enhanced for colon.
446     * @since 5.5
447     */
448    public void setIgnoreEnhancedForColon(boolean ignore) {
449        ignoreEnhancedForColon = ignore;
450    }
451
452    /**
453     * Setter to allow empty class, interface and enum bodies.
454     *
455     * @param allow {@code true} to allow empty type bodies.
456     * @since 5.8
457     */
458    public void setAllowEmptyTypes(boolean allow) {
459        allowEmptyTypes = allow;
460    }
461
462    /**
463     * Setter to allow empty loop bodies.
464     *
465     * @param allow {@code true} to allow empty loops bodies.
466     * @since 5.8
467     */
468    public void setAllowEmptyLoops(boolean allow) {
469        allowEmptyLoops = allow;
470    }
471
472    /**
473     * Setter to allow empty lambda bodies.
474     *
475     * @param allow {@code true} to allow empty lambda expressions.
476     * @since 6.14
477     */
478    public void setAllowEmptyLambdas(boolean allow) {
479        allowEmptyLambdas = allow;
480    }
481
482    /**
483     * Setter to allow empty catch bodies.
484     *
485     * @param allow {@code true} to allow empty catch blocks.
486     * @since 7.6
487     */
488    public void setAllowEmptyCatches(boolean allow) {
489        allowEmptyCatches = allow;
490    }
491
492    /**
493     * Setter to allow empty switch blocks and block statements.
494     *
495     * @param allow {@code true} to allow empty switch case and default blocks.
496     * @since 10.19.0
497     */
498    public void setAllowEmptySwitchBlockStatements(boolean allow) {
499        allowEmptySwitchBlockStatements = allow;
500    }
501
502    @Override
503    public void visitToken(DetailAST ast) {
504        final int currentType = ast.getType();
505        if (!isNotRelevantSituation(ast, currentType)) {
506            final int[] line = getLineCodePoints(ast.getLineNo() - 1);
507            final int before = ast.getColumnNo() - 1;
508            final int after = ast.getColumnNo() + ast.getText().length();
509
510            if (before >= 0 && shouldCheckSeparationFromPreviousToken(ast)
511                        && !CommonUtil.isCodePointWhitespace(line, before)) {
512                log(ast, MSG_WS_NOT_PRECEDED, ast.getText());
513            }
514
515            if (after < line.length) {
516                final char nextChar = Character.toChars(line[after])[0];
517                if (shouldCheckSeparationFromNextToken(ast, nextChar)
518                        && !Character.isWhitespace(nextChar)) {
519                    log(ast, MSG_WS_NOT_FOLLOWED, ast.getText());
520                }
521            }
522        }
523    }
524
525    /**
526     * Is ast not a target of Check.
527     *
528     * @param ast ast
529     * @param currentType type of ast
530     * @return true is ok to skip validation
531     */
532    private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
533        final int parentType = ast.getParent().getType();
534        final boolean result;
535        switch (parentType) {
536            case TokenTypes.DOT:
537                result = currentType == TokenTypes.STAR;
538                break;
539            case TokenTypes.LITERAL_DEFAULT:
540            case TokenTypes.LITERAL_CASE:
541            case TokenTypes.CASE_GROUP:
542                result = true;
543                break;
544            case TokenTypes.FOR_EACH_CLAUSE:
545                result = ignoreEnhancedForColon;
546                break;
547            case TokenTypes.EXPR:
548                result = currentType == TokenTypes.LITERAL_SWITCH;
549                break;
550            case TokenTypes.ARRAY_INIT:
551            case TokenTypes.ANNOTATION_ARRAY_INIT:
552                result = currentType == TokenTypes.RCURLY;
553                break;
554            default:
555                result = isEmptyBlock(ast, parentType)
556                    || allowEmptyTypes && isEmptyType(ast);
557        }
558        return result;
559    }
560
561    /**
562     * Check if it should be checked if previous token is separated from current by
563     * whitespace.
564     * This function is needed to recognise double brace initialization as valid,
565     * unfortunately it's not possible to implement this functionality
566     * in isNotRelevantSituation method, because in this method when we return
567     * true(is not relevant) ast is later doesn't check at all. For example:
568     * new Properties() {{setProperty("double curly braces", "are not a style violation");
569     * }};
570     * For second left curly brace in first line when we would return true from
571     * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
572     * is not separated from previous token.
573     *
574     * @param ast current AST.
575     * @return true if it should be checked if previous token is separated by whitespace,
576     *      false otherwise.
577     */
578    private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
579        return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
580    }
581
582    /**
583     * Check if it should be checked if next token is separated from current by
584     * whitespace. Explanation why this method is needed is identical to one
585     * included in shouldCheckSeparationFromPreviousToken method.
586     *
587     * @param ast current AST.
588     * @param nextChar next character.
589     * @return true if it should be checked if next token is separated by whitespace,
590     *      false otherwise.
591     */
592    private boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
593        return !isEmptyCtorBlockCheckedFromSlist(ast)
594                && !(ast.getType() == TokenTypes.LITERAL_RETURN
595                && ast.getFirstChild().getType() == TokenTypes.SEMI)
596                && ast.getType() != TokenTypes.ARRAY_INIT
597                && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
598                && !isPartOfDoubleBraceInitializerForNextToken(ast);
599    }
600
601    /**
602     * Check for "})" or "};" or "},". Happens with anon-inners
603     *
604     * @param currentType token
605     * @param nextChar next symbol
606     * @return true is that is end of anon inner class
607     */
608    private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
609        return currentType == TokenTypes.RCURLY
610                && (nextChar == ')'
611                        || nextChar == ';'
612                        || nextChar == ','
613                        || nextChar == '.');
614    }
615
616    /**
617     * Is empty block.
618     *
619     * @param ast ast
620     * @param parentType parent
621     * @return true is block is empty
622     */
623    private boolean isEmptyBlock(DetailAST ast, int parentType) {
624        return isEmptyMethodBlock(ast, parentType)
625                || isEmptyCtorBlockCheckedFromRcurly(ast)
626                || isEmptyLoop(ast, parentType)
627                || isEmptyLambda(ast, parentType)
628                || isEmptyCatch(ast, parentType)
629                || isEmptySwitchBlockStatement(ast);
630    }
631
632    /**
633     * Tests if a given {@code DetailAST} is part of an empty block.
634     * An example empty block might look like the following
635     * <pre>   public void myMethod(int val) {}</pre>
636     * In the above, the method body is an empty block ("{}").
637     *
638     * @param ast the {@code DetailAST} to test.
639     * @param parentType the token type of {@code ast}'s parent.
640     * @param match the parent token type we're looking to match.
641     * @return {@code true} if {@code ast} makes up part of an
642     *         empty block contained under a {@code match} token type
643     *         node.
644     */
645    private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
646        final boolean result;
647        final int type = ast.getType();
648        if (type == TokenTypes.RCURLY) {
649            final DetailAST parent = ast.getParent();
650            final DetailAST grandParent = ast.getParent().getParent();
651            result = parent.getFirstChild().getType() == TokenTypes.RCURLY
652                    && grandParent.getType() == match;
653        }
654        else {
655            result = type == TokenTypes.SLIST
656                && parentType == match
657                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
658        }
659        return result;
660    }
661
662    /**
663     * Test if the given {@code DetailAST} is part of an allowed empty
664     * method block.
665     *
666     * @param ast the {@code DetailAST} to test.
667     * @param parentType the token type of {@code ast}'s parent.
668     * @return {@code true} if {@code ast} makes up part of an
669     *         allowed empty method block.
670     */
671    private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
672        return allowEmptyMethods
673                && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
674    }
675
676    /**
677     * Test if the given {@code DetailAST} is part of an allowed empty
678     * constructor (ctor) block checked from RCURLY.
679     *
680     * @param ast the {@code DetailAST} to test.
681     * @return {@code true} if {@code ast} makes up part of an
682     *         allowed empty constructor block.
683     */
684    private boolean isEmptyCtorBlockCheckedFromRcurly(DetailAST ast) {
685        final DetailAST parent = ast.getParent();
686        final DetailAST grandParent = ast.getParent().getParent();
687        return allowEmptyConstructors
688                && parent.getFirstChild().getType() == TokenTypes.RCURLY
689                && (grandParent.getType() == TokenTypes.CTOR_DEF
690                        || grandParent.getType() == TokenTypes.COMPACT_CTOR_DEF);
691
692    }
693
694    /**
695     * Test if the given {@code DetailAST} is a part of an allowed
696     * empty constructor checked from SLIST token.
697     *
698     * @param ast the {@code DetailAST} to test.
699     * @return {@code true} if {@code ast} makes up part of an
700     *          empty constructor block.
701     */
702    private boolean isEmptyCtorBlockCheckedFromSlist(DetailAST ast) {
703        return allowEmptyConstructors
704                && (ast.getParent().getType() == TokenTypes.CTOR_DEF
705                        || ast.getParent().getType() == TokenTypes.COMPACT_CTOR_DEF)
706                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
707    }
708
709    /**
710     * Checks if loop is empty.
711     *
712     * @param ast ast the {@code DetailAST} to test.
713     * @param parentType the token type of {@code ast}'s parent.
714     * @return {@code true} if {@code ast} makes up part of an
715     *         allowed empty loop block.
716     */
717    private boolean isEmptyLoop(DetailAST ast, int parentType) {
718        return allowEmptyLoops
719                && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
720                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
721                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
722    }
723
724    /**
725     * Test if the given {@code DetailAST} is part of an allowed empty
726     * lambda block.
727     *
728     * @param ast the {@code DetailAST} to test.
729     * @param parentType the token type of {@code ast}'s parent.
730     * @return {@code true} if {@code ast} makes up part of an
731     *         allowed empty lambda block.
732     */
733    private boolean isEmptyLambda(DetailAST ast, int parentType) {
734        return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
735    }
736
737    /**
738     * Tests if the given {@code DetailAst} is part of an allowed empty
739     * catch block.
740     *
741     * @param ast the {@code DetailAst} to test.
742     * @param parentType the token type of {@code ast}'s parent
743     * @return {@code true} if {@code ast} makes up part of an
744     *         allowed empty catch block.
745     */
746    private boolean isEmptyCatch(DetailAST ast, int parentType) {
747        return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
748    }
749
750    /**
751     * Tests if the given {@code DetailAst} is part of an allowed empty
752     * switch case or default block.
753     *
754     * @param ast the {@code DetailAst} to test.
755     * @return {@code true} if {@code ast} makes up part of an allowed
756     *         empty switch case or default block.
757     */
758    private boolean isEmptySwitchBlockStatement(DetailAST ast) {
759        final boolean isEmptySwitchBlockStatement;
760
761        if (allowEmptySwitchBlockStatements) {
762            final DetailAST parent = ast.getParent();
763            final DetailAST grandParent = parent.getParent();
764
765            final boolean isEmptyCaseInSwitchRule =
766                    isEmptyBlock(ast, parent.getType(), TokenTypes.SWITCH_RULE);
767
768            final boolean isEmptyCaseGroupCheckedFromLcurly =
769                    isEmptyBlock(ast, grandParent.getType(), TokenTypes.CASE_GROUP);
770
771            final boolean isEmptyCaseGroupCheckedFromRcurly =
772                    parent.getFirstChild().getType() == TokenTypes.RCURLY
773                      && grandParent.getParent().getType() == TokenTypes.CASE_GROUP;
774
775            isEmptySwitchBlockStatement = isEmptyCaseInSwitchRule
776                    || isEmptyCaseGroupCheckedFromLcurly || isEmptyCaseGroupCheckedFromRcurly;
777        }
778        else {
779            isEmptySwitchBlockStatement = false;
780        }
781
782        return isEmptySwitchBlockStatement;
783    }
784
785    /**
786     * Test if the given {@code DetailAST} is part of an empty block.
787     * An example empty block might look like the following
788     * <pre>   class Foo {}</pre>
789     *
790     * @param ast ast the {@code DetailAST} to test.
791     * @return {@code true} if {@code ast} makes up part of an
792     *         empty block contained under a {@code match} token type
793     *         node.
794     */
795    private static boolean isEmptyType(DetailAST ast) {
796        final int type = ast.getType();
797        final DetailAST nextSibling = ast.getNextSibling();
798        final DetailAST previousSibling = ast.getPreviousSibling();
799        return type == TokenTypes.LCURLY
800                    && nextSibling.getType() == TokenTypes.RCURLY
801                || previousSibling != null
802                    && previousSibling.getType() == TokenTypes.LCURLY;
803    }
804
805    /**
806     * Check if given ast is part of double brace initializer and if it
807     * should omit checking if previous token is separated by whitespace.
808     *
809     * @param ast ast to check
810     * @return true if it should omit checking for previous token, false otherwise
811     */
812    private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
813        final boolean initializerBeginsAfterClassBegins =
814                ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
815        final boolean classEndsAfterInitializerEnds = ast.getPreviousSibling() != null
816                && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
817        return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
818    }
819
820    /**
821     * Check if given ast is part of double brace initializer and if it
822     * should omit checking if next token is separated by whitespace.
823     * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
824     * PR#2845</a> for more information why this function was needed.
825     *
826     * @param ast ast to check
827     * @return true if it should omit checking for next token, false otherwise
828     */
829    private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
830        final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
831            && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
832        final boolean initializerEndsBeforeClassEnds =
833            ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
834            && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
835        return classBeginBeforeInitializerBegin || initializerEndsBeforeClassEnds;
836    }
837
838}