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