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