001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.api.AbstractCheck;
023import com.puppycrawl.tools.checkstyle.api.DetailAST;
024import com.puppycrawl.tools.checkstyle.api.TokenTypes;
025import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
026
027/**
028 * Checks that a token is surrounded by whitespace.
029 *
030 * <p>By default the check will check the following operators:
031 *  {@link TokenTypes#LITERAL_ASSERT ASSERT},
032 *  {@link TokenTypes#ASSIGN ASSIGN},
033 *  {@link TokenTypes#BAND BAND},
034 *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
035 *  {@link TokenTypes#BOR BOR},
036 *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
037 *  {@link TokenTypes#BSR BSR},
038 *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
039 *  {@link TokenTypes#BXOR BXOR},
040 *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
041 *  {@link TokenTypes#COLON COLON},
042 *  {@link TokenTypes#DIV DIV},
043 *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
044 *  {@link TokenTypes#DO_WHILE DO_WHILE},
045 *  {@link TokenTypes#EQUAL EQUAL},
046 *  {@link TokenTypes#GE GE},
047 *  {@link TokenTypes#GT GT},
048 *  {@link TokenTypes#LAND LAND},
049 *  {@link TokenTypes#LCURLY LCURLY},
050 *  {@link TokenTypes#LE LE},
051 *  {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
052 *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
053 *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
054 *  {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY},
055 *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
056 *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
057 *  {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN},
058 *  {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH},
059 *  {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED},
060 *  {@link TokenTypes#LITERAL_TRY LITERAL_TRY},
061 *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
062 *  {@link TokenTypes#LOR LOR},
063 *  {@link TokenTypes#LT LT},
064 *  {@link TokenTypes#MINUS MINUS},
065 *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
066 *  {@link TokenTypes#MOD MOD},
067 *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
068 *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
069 *  {@link TokenTypes#PLUS PLUS},
070 *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
071 *  {@link TokenTypes#QUESTION QUESTION},
072 *  {@link TokenTypes#RCURLY RCURLY},
073 *  {@link TokenTypes#SL SL},
074 *  {@link TokenTypes#SLIST SLIST},
075 *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
076 *  {@link TokenTypes#SR SR},
077 *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
078 *  {@link TokenTypes#STAR STAR},
079 *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN},
080 *  {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT},
081 *  {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}.
082 *
083 * <p>An example of how to configure the check is:
084 *
085 * <pre>
086 * &lt;module name="WhitespaceAround"/&gt;
087 * </pre>
088 *
089 * <p>An example of how to configure the check for whitespace only around
090 * assignment operators is:
091 *
092 * <pre>
093 * &lt;module name="WhitespaceAround"&gt;
094 *     &lt;property name="tokens"
095 *               value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,
096 *                      MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,
097 *                      BOR_ASSIGN,BAND_ASSIGN"/&gt;
098 * &lt;/module&gt;
099 * </pre>
100 *
101 * <p>An example of how to configure the check for whitespace only around
102 * curly braces is:
103 * <pre>
104 * &lt;module name="WhitespaceAround"&gt;
105 *     &lt;property name="tokens"
106 *               value="LCURLY,RCURLY"/&gt;
107 * &lt;/module&gt;
108 * </pre>
109 *
110 * <p>In addition, this check can be configured to allow empty methods, types,
111 * for, while, do-while loops, lambdas and constructor bodies.
112 * For example:
113 *
114 * <pre>{@code
115 * public MyClass() {}      // empty constructor
116 * public void func() {}    // empty method
117 * public interface Foo {} // empty interface
118 * public class Foo {} // empty class
119 * public enum Foo {} // empty enum
120 * MyClass c = new MyClass() {}; // empty anonymous class
121 * while (i = 1) {} // empty while loop
122 * for (int i = 1; i &gt; 1; i++) {} // empty for loop
123 * do {} while (i = 1); // empty do-while loop
124 * Runnable noop = () -> {}; // empty lambda
125 * public @interface Beta {} // empty annotation type
126 * }</pre>
127 *
128 * <p>This check does not flag as violation double brace initialization like:</p>
129 * <pre>
130 *   new Properties() {{
131 *     setProperty("key", "value");
132 *   }};
133 * </pre>
134 *
135 * <p>To configure the check to allow empty method blocks use
136 *
137 * <pre>   &lt;property name="allowEmptyMethods" value="true" /&gt;</pre>
138 *
139 * <p>To configure the check to allow empty constructor blocks use
140 *
141 * <pre>   &lt;property name="allowEmptyConstructors" value="true" /&gt;</pre>
142 *
143 * <p>To configure the check to allow empty type blocks use
144 *
145 * <pre>   &lt;property name="allowEmptyTypes" value="true" /&gt;</pre>
146 *
147 * <p>To configure the check to allow empty loop blocks use
148 *
149 * <pre>   &lt;property name="allowEmptyLoops" value="true" /&gt;</pre>
150 *
151 * <p>To configure the check to allow empty lambdas blocks use
152 *
153 * <pre>   &lt;property name="allowEmptyLambdas" value="true" /&gt;</pre>
154 *
155 * <p>Also, this check can be configured to ignore the colon in an enhanced for
156 * loop. The colon in an enhanced for loop is ignored by default
157 *
158 * <p>To configure the check to ignore the colon
159 *
160 * <pre>   &lt;property name="ignoreEnhancedForColon" value="true" /&gt;</pre>
161 *
162 * @author Oliver Burn
163 * @author maxvetrenko
164 * @author Andrei Selkin
165 */
166public class WhitespaceAroundCheck extends AbstractCheck {
167
168    /**
169     * A key is pointing to the warning message text in "messages.properties"
170     * file.
171     */
172    public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
173
174    /**
175     * A key is pointing to the warning message text in "messages.properties"
176     * file.
177     */
178    public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
179
180    /** Whether or not empty constructor bodies are allowed. */
181    private boolean allowEmptyConstructors;
182    /** Whether or not empty method bodies are allowed. */
183    private boolean allowEmptyMethods;
184    /** Whether or not empty classes, enums and interfaces are allowed. */
185    private boolean allowEmptyTypes;
186    /** Whether or not empty loops are allowed. */
187    private boolean allowEmptyLoops;
188    /** Whether or not empty lambda blocks are allowed. */
189    private boolean allowEmptyLambdas;
190    /** Whether or not empty catch blocks are allowed. */
191    private boolean allowEmptyCatches;
192    /** Whether or not to ignore a colon in a enhanced for loop. */
193    private boolean ignoreEnhancedForColon = true;
194
195    @Override
196    public int[] getDefaultTokens() {
197        return new int[] {
198            TokenTypes.ASSIGN,
199            TokenTypes.BAND,
200            TokenTypes.BAND_ASSIGN,
201            TokenTypes.BOR,
202            TokenTypes.BOR_ASSIGN,
203            TokenTypes.BSR,
204            TokenTypes.BSR_ASSIGN,
205            TokenTypes.BXOR,
206            TokenTypes.BXOR_ASSIGN,
207            TokenTypes.COLON,
208            TokenTypes.DIV,
209            TokenTypes.DIV_ASSIGN,
210            TokenTypes.DO_WHILE,
211            TokenTypes.EQUAL,
212            TokenTypes.GE,
213            TokenTypes.GT,
214            TokenTypes.LAMBDA,
215            TokenTypes.LAND,
216            TokenTypes.LCURLY,
217            TokenTypes.LE,
218            TokenTypes.LITERAL_CATCH,
219            TokenTypes.LITERAL_DO,
220            TokenTypes.LITERAL_ELSE,
221            TokenTypes.LITERAL_FINALLY,
222            TokenTypes.LITERAL_FOR,
223            TokenTypes.LITERAL_IF,
224            TokenTypes.LITERAL_RETURN,
225            TokenTypes.LITERAL_SWITCH,
226            TokenTypes.LITERAL_SYNCHRONIZED,
227            TokenTypes.LITERAL_TRY,
228            TokenTypes.LITERAL_WHILE,
229            TokenTypes.LOR,
230            TokenTypes.LT,
231            TokenTypes.MINUS,
232            TokenTypes.MINUS_ASSIGN,
233            TokenTypes.MOD,
234            TokenTypes.MOD_ASSIGN,
235            TokenTypes.NOT_EQUAL,
236            TokenTypes.PLUS,
237            TokenTypes.PLUS_ASSIGN,
238            TokenTypes.QUESTION,
239            TokenTypes.RCURLY,
240            TokenTypes.SL,
241            TokenTypes.SLIST,
242            TokenTypes.SL_ASSIGN,
243            TokenTypes.SR,
244            TokenTypes.SR_ASSIGN,
245            TokenTypes.STAR,
246            TokenTypes.STAR_ASSIGN,
247            TokenTypes.LITERAL_ASSERT,
248            TokenTypes.TYPE_EXTENSION_AND,
249        };
250    }
251
252    @Override
253    public int[] getAcceptableTokens() {
254        return new int[] {
255            TokenTypes.ASSIGN,
256            TokenTypes.ARRAY_INIT,
257            TokenTypes.BAND,
258            TokenTypes.BAND_ASSIGN,
259            TokenTypes.BOR,
260            TokenTypes.BOR_ASSIGN,
261            TokenTypes.BSR,
262            TokenTypes.BSR_ASSIGN,
263            TokenTypes.BXOR,
264            TokenTypes.BXOR_ASSIGN,
265            TokenTypes.COLON,
266            TokenTypes.DIV,
267            TokenTypes.DIV_ASSIGN,
268            TokenTypes.DO_WHILE,
269            TokenTypes.EQUAL,
270            TokenTypes.GE,
271            TokenTypes.GT,
272            TokenTypes.LAMBDA,
273            TokenTypes.LAND,
274            TokenTypes.LCURLY,
275            TokenTypes.LE,
276            TokenTypes.LITERAL_CATCH,
277            TokenTypes.LITERAL_DO,
278            TokenTypes.LITERAL_ELSE,
279            TokenTypes.LITERAL_FINALLY,
280            TokenTypes.LITERAL_FOR,
281            TokenTypes.LITERAL_IF,
282            TokenTypes.LITERAL_RETURN,
283            TokenTypes.LITERAL_SWITCH,
284            TokenTypes.LITERAL_SYNCHRONIZED,
285            TokenTypes.LITERAL_TRY,
286            TokenTypes.LITERAL_WHILE,
287            TokenTypes.LOR,
288            TokenTypes.LT,
289            TokenTypes.MINUS,
290            TokenTypes.MINUS_ASSIGN,
291            TokenTypes.MOD,
292            TokenTypes.MOD_ASSIGN,
293            TokenTypes.NOT_EQUAL,
294            TokenTypes.PLUS,
295            TokenTypes.PLUS_ASSIGN,
296            TokenTypes.QUESTION,
297            TokenTypes.RCURLY,
298            TokenTypes.SL,
299            TokenTypes.SLIST,
300            TokenTypes.SL_ASSIGN,
301            TokenTypes.SR,
302            TokenTypes.SR_ASSIGN,
303            TokenTypes.STAR,
304            TokenTypes.STAR_ASSIGN,
305            TokenTypes.LITERAL_ASSERT,
306            TokenTypes.TYPE_EXTENSION_AND,
307            TokenTypes.WILDCARD_TYPE,
308            TokenTypes.GENERIC_START,
309            TokenTypes.GENERIC_END,
310            TokenTypes.ELLIPSIS,
311        };
312    }
313
314    @Override
315    public int[] getRequiredTokens() {
316        return CommonUtils.EMPTY_INT_ARRAY;
317    }
318
319    /**
320     * Sets whether or not empty method bodies are allowed.
321     * @param allow {@code true} to allow empty method bodies.
322     */
323    public void setAllowEmptyMethods(boolean allow) {
324        allowEmptyMethods = allow;
325    }
326
327    /**
328     * Sets whether or not empty constructor bodies are allowed.
329     * @param allow {@code true} to allow empty constructor bodies.
330     */
331    public void setAllowEmptyConstructors(boolean allow) {
332        allowEmptyConstructors = allow;
333    }
334
335    /**
336     * Sets whether or not to ignore the whitespace around the
337     * colon in an enhanced for loop.
338     * @param ignore {@code true} to ignore enhanced for colon.
339     */
340    public void setIgnoreEnhancedForColon(boolean ignore) {
341        ignoreEnhancedForColon = ignore;
342    }
343
344    /**
345     * Sets whether or not empty type bodies are allowed.
346     * @param allow {@code true} to allow empty type bodies.
347     */
348    public void setAllowEmptyTypes(boolean allow) {
349        allowEmptyTypes = allow;
350    }
351
352    /**
353     * Sets whether or not empty loop bodies are allowed.
354     * @param allow {@code true} to allow empty loops bodies.
355     */
356    public void setAllowEmptyLoops(boolean allow) {
357        allowEmptyLoops = allow;
358    }
359
360    /**
361     * Sets whether or not empty lambdas bodies are allowed.
362     * @param allow {@code true} to allow empty lambda expressions.
363     */
364    public void setAllowEmptyLambdas(boolean allow) {
365        allowEmptyLambdas = allow;
366    }
367
368    /**
369     * Sets whether or not empty catch blocks are allowed.
370     * @param allow {@code true} to allow empty catch blocks.
371     */
372    public void setAllowEmptyCatches(boolean allow) {
373        allowEmptyCatches = allow;
374    }
375
376    @Override
377    public void visitToken(DetailAST ast) {
378        final int currentType = ast.getType();
379        if (!isNotRelevantSituation(ast, currentType)) {
380            final String line = getLine(ast.getLineNo() - 1);
381            final int before = ast.getColumnNo() - 1;
382            final int after = ast.getColumnNo() + ast.getText().length();
383
384            if (before >= 0) {
385                final char prevChar = line.charAt(before);
386                if (shouldCheckSeparationFromPreviousToken(ast)
387                        && !Character.isWhitespace(prevChar)) {
388                    log(ast.getLineNo(), ast.getColumnNo(),
389                            MSG_WS_NOT_PRECEDED, ast.getText());
390                }
391            }
392
393            if (after < line.length()) {
394                final char nextChar = line.charAt(after);
395                if (shouldCheckSeparationFromNextToken(ast, nextChar)
396                        && !Character.isWhitespace(nextChar)) {
397                    log(ast.getLineNo(), ast.getColumnNo() + ast.getText().length(),
398                            MSG_WS_NOT_FOLLOWED, ast.getText());
399                }
400            }
401        }
402    }
403
404    /**
405     * Is ast not a target of Check.
406     * @param ast ast
407     * @param currentType type of ast
408     * @return true is ok to skip validation
409     */
410    private boolean isNotRelevantSituation(DetailAST ast, int currentType) {
411        final int parentType = ast.getParent().getType();
412        final boolean starImport = currentType == TokenTypes.STAR
413                && parentType == TokenTypes.DOT;
414        final boolean slistInsideCaseGroup = currentType == TokenTypes.SLIST
415                && parentType == TokenTypes.CASE_GROUP;
416
417        final boolean starImportOrSlistInsideCaseGroup = starImport || slistInsideCaseGroup;
418        final boolean colonOfCaseOrDefaultOrForEach =
419                isColonOfCaseOrDefault(currentType, parentType)
420                        || isColonOfForEach(currentType, parentType);
421        final boolean emptyBlockOrType =
422                isEmptyBlock(ast, parentType)
423                    || allowEmptyTypes && isEmptyType(ast);
424
425        return starImportOrSlistInsideCaseGroup
426                || colonOfCaseOrDefaultOrForEach
427                || emptyBlockOrType
428                || isArrayInitialization(currentType, parentType);
429    }
430
431    /**
432     * Check if it should be checked if previous token is separated from current by
433     * whitespace.
434     * This function is needed to recognise double brace initialization as valid,
435     * unfortunately its not possible to implement this functionality
436     * in isNotRelevantSituation method, because in this method when we return
437     * true(is not relevant) ast is later doesn't check at all. For example:
438     * new Properties() {{setProperty("double curly braces", "are not a style error");
439     * }};
440     * For second left curly brace in first line when we would return true from
441     * isNotRelevantSituation it wouldn't later check that the next token(setProperty)
442     * is not separated from previous token.
443     * @param ast current AST.
444     * @return true if it should be checked if previous token is separated by whitespace,
445     *      false otherwise.
446     */
447    private static boolean shouldCheckSeparationFromPreviousToken(DetailAST ast) {
448        return !isPartOfDoubleBraceInitializerForPreviousToken(ast);
449    }
450
451    /**
452     * Check if it should be checked if next token is separated from current by
453     * whitespace. Explanation why this method is needed is identical to one
454     * included in shouldCheckSeparationFromPreviousToken method.
455     * @param ast current AST.
456     * @param nextChar next character.
457     * @return true if it should be checked if next token is separated by whitespace,
458     *      false otherwise.
459     */
460    private static boolean shouldCheckSeparationFromNextToken(DetailAST ast, char nextChar) {
461        return !(ast.getType() == TokenTypes.LITERAL_RETURN
462                    && ast.getFirstChild().getType() == TokenTypes.SEMI)
463                && ast.getType() != TokenTypes.ARRAY_INIT
464                && !isAnonymousInnerClassEnd(ast.getType(), nextChar)
465                && !isPartOfDoubleBraceInitializerForNextToken(ast);
466    }
467
468    /**
469     * Check for "})" or "};" or "},". Happens with anon-inners
470     * @param currentType token
471     * @param nextChar next symbol
472     * @return true is that is end of anon inner class
473     */
474    private static boolean isAnonymousInnerClassEnd(int currentType, char nextChar) {
475        return currentType == TokenTypes.RCURLY
476                && (nextChar == ')'
477                        || nextChar == ';'
478                        || nextChar == ','
479                        || nextChar == '.');
480    }
481
482    /**
483     * Is empty block.
484     * @param ast ast
485     * @param parentType parent
486     * @return true is block is empty
487     */
488    private boolean isEmptyBlock(DetailAST ast, int parentType) {
489        return isEmptyMethodBlock(ast, parentType)
490                || isEmptyCtorBlock(ast, parentType)
491                || isEmptyLoop(ast, parentType)
492                || isEmptyLambda(ast, parentType)
493                || isEmptyCatch(ast, parentType);
494    }
495
496    /**
497     * Tests if a given {@code DetailAST} is part of an empty block.
498     * An example empty block might look like the following
499     * <p>
500     * <pre>   public void myMethod(int val) {}</pre>
501     * </p>
502     * In the above, the method body is an empty block ("{}").
503     *
504     * @param ast the {@code DetailAST} to test.
505     * @param parentType the token type of {@code ast}'s parent.
506     * @param match the parent token type we're looking to match.
507     * @return {@code true} if {@code ast} makes up part of an
508     *         empty block contained under a {@code match} token type
509     *         node.
510     */
511    private static boolean isEmptyBlock(DetailAST ast, int parentType, int match) {
512        final boolean result;
513        final int type = ast.getType();
514        if (type == TokenTypes.RCURLY) {
515            final DetailAST parent = ast.getParent();
516            final DetailAST grandParent = ast.getParent().getParent();
517            result = parentType == TokenTypes.SLIST
518                    && parent.getFirstChild().getType() == TokenTypes.RCURLY
519                    && grandParent.getType() == match;
520        }
521        else {
522            result = type == TokenTypes.SLIST
523                && parentType == match
524                && ast.getFirstChild().getType() == TokenTypes.RCURLY;
525        }
526        return result;
527    }
528
529    /**
530     * Whether colon belongs to cases or defaults.
531     * @param currentType current
532     * @param parentType parent
533     * @return true if current token in colon of case or default tokens
534     */
535    private static boolean isColonOfCaseOrDefault(int currentType, int parentType) {
536        return currentType == TokenTypes.COLON
537                && (parentType == TokenTypes.LITERAL_DEFAULT
538                        || parentType == TokenTypes.LITERAL_CASE);
539    }
540
541    /**
542     * Whether colon belongs to for-each.
543     * @param currentType current
544     * @param parentType parent
545     * @return true if current token in colon of for-each token
546     */
547    private boolean isColonOfForEach(int currentType, int parentType) {
548        return currentType == TokenTypes.COLON
549                && parentType == TokenTypes.FOR_EACH_CLAUSE
550                && ignoreEnhancedForColon;
551    }
552
553    /**
554     * Is array initialization.
555     * @param currentType current token
556     * @param parentType parent token
557     * @return true is current token inside array initialization
558     */
559    private static boolean isArrayInitialization(int currentType, int parentType) {
560        return (currentType == TokenTypes.RCURLY || currentType == TokenTypes.LCURLY)
561                && (parentType == TokenTypes.ARRAY_INIT
562                        || parentType == TokenTypes.ANNOTATION_ARRAY_INIT);
563    }
564
565    /**
566     * Test if the given {@code DetailAST} is part of an allowed empty
567     * method block.
568     * @param ast the {@code DetailAST} to test.
569     * @param parentType the token type of {@code ast}'s parent.
570     * @return {@code true} if {@code ast} makes up part of an
571     *         allowed empty method block.
572     */
573    private boolean isEmptyMethodBlock(DetailAST ast, int parentType) {
574        return allowEmptyMethods
575                && isEmptyBlock(ast, parentType, TokenTypes.METHOD_DEF);
576    }
577
578    /**
579     * Test if the given {@code DetailAST} is part of an allowed empty
580     * constructor (ctor) block.
581     * @param ast the {@code DetailAST} to test.
582     * @param parentType the token type of {@code ast}'s parent.
583     * @return {@code true} if {@code ast} makes up part of an
584     *         allowed empty constructor block.
585     */
586    private boolean isEmptyCtorBlock(DetailAST ast, int parentType) {
587        return allowEmptyConstructors
588                && isEmptyBlock(ast, parentType, TokenTypes.CTOR_DEF);
589    }
590
591    /**
592     * Checks if loop is empty.
593     * @param ast ast the {@code DetailAST} to test.
594     * @param parentType the token type of {@code ast}'s parent.
595     * @return {@code true} if {@code ast} makes up part of an
596     *         allowed empty loop block.
597     */
598    private boolean isEmptyLoop(DetailAST ast, int parentType) {
599        return allowEmptyLoops
600                && (isEmptyBlock(ast, parentType, TokenTypes.LITERAL_FOR)
601                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_WHILE)
602                        || isEmptyBlock(ast, parentType, TokenTypes.LITERAL_DO));
603    }
604
605    /**
606     * Test if the given {@code DetailAST} is part of an allowed empty
607     * lambda block.
608     * @param ast the {@code DetailAST} to test.
609     * @param parentType the token type of {@code ast}'s parent.
610     * @return {@code true} if {@code ast} makes up part of an
611     *         allowed empty lambda block.
612     */
613    private boolean isEmptyLambda(DetailAST ast, int parentType) {
614        return allowEmptyLambdas && isEmptyBlock(ast, parentType, TokenTypes.LAMBDA);
615    }
616
617    /**
618     * Tests if the given {@code DetailAst} is part of an allowed empty
619     * catch block.
620     * @param ast the {@code DetailAst} to test.
621     * @param parentType the token type of {@code ast}'s parent
622     * @return {@code true} if {@code ast} makes up part of an
623     *         allowed empty catch block.
624     */
625    private boolean isEmptyCatch(DetailAST ast, int parentType) {
626        return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
627    }
628
629    /**
630     * Test if the given {@code DetailAST} is part of an empty block.
631     * An example empty block might look like the following
632     * <p>
633     * <pre>   class Foo {}</pre>
634     * </p>
635     *
636     * @param ast ast the {@code DetailAST} to test.
637     * @return {@code true} if {@code ast} makes up part of an
638     *         empty block contained under a {@code match} token type
639     *         node.
640     */
641    private static boolean isEmptyType(DetailAST ast) {
642        final int type = ast.getType();
643        final DetailAST nextSibling = ast.getNextSibling();
644        final DetailAST previousSibling = ast.getPreviousSibling();
645        return type == TokenTypes.LCURLY
646                    && nextSibling.getType() == TokenTypes.RCURLY
647                || type == TokenTypes.RCURLY
648                    && previousSibling != null
649                    && previousSibling.getType() == TokenTypes.LCURLY;
650    }
651
652    /**
653     * Check if given ast is part of double brace initializer and if it
654     * should omit checking if previous token is separated by whitespace.
655     * @param ast ast to check
656     * @return true if it should omit checking for previous token, false otherwise
657     */
658    private static boolean isPartOfDoubleBraceInitializerForPreviousToken(DetailAST ast) {
659        final boolean initializerBeginsAfterClassBegins = ast.getType() == TokenTypes.SLIST
660                && ast.getParent().getType() == TokenTypes.INSTANCE_INIT;
661        final boolean classEndsAfterInitializerEnds = ast.getType() == TokenTypes.RCURLY
662                && ast.getPreviousSibling() != null
663                && ast.getPreviousSibling().getType() == TokenTypes.INSTANCE_INIT;
664        return initializerBeginsAfterClassBegins || classEndsAfterInitializerEnds;
665    }
666
667    /**
668     * Check if given ast is part of double brace initializer and if it
669     * should omit checking if next token is separated by whitespace.
670     * See <a href="https://github.com/checkstyle/checkstyle/pull/2845">
671     * PR#2845</a> for more information why this function was needed.
672     * @param ast ast to check
673     * @return true if it should omit checking for next token, false otherwise
674     */
675    private static boolean isPartOfDoubleBraceInitializerForNextToken(DetailAST ast) {
676        final boolean classBeginBeforeInitializerBegin = ast.getType() == TokenTypes.LCURLY
677            && ast.getNextSibling().getType() == TokenTypes.INSTANCE_INIT;
678        final boolean initalizerEndsBeforeClassEnds = ast.getType() == TokenTypes.RCURLY
679            && ast.getParent().getType() == TokenTypes.SLIST
680            && ast.getParent().getParent().getType() == TokenTypes.INSTANCE_INIT
681            && ast.getParent().getParent().getNextSibling().getType() == TokenTypes.RCURLY;
682        return classBeginBeforeInitializerBegin || initalizerEndsBeforeClassEnds;
683    }
684}