View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.whitespace;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
27  
28  /**
29   * Checks that a token is surrounded by whitespace.
30   *
31   * <p>By default the check will check the following operators:
32   *  {@link TokenTypes#LITERAL_ASSERT ASSERT},
33   *  {@link TokenTypes#ASSIGN ASSIGN},
34   *  {@link TokenTypes#BAND BAND},
35   *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
36   *  {@link TokenTypes#BOR BOR},
37   *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
38   *  {@link TokenTypes#BSR BSR},
39   *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
40   *  {@link TokenTypes#BXOR BXOR},
41   *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
42   *  {@link TokenTypes#COLON COLON},
43   *  {@link TokenTypes#DIV DIV},
44   *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
45   *  {@link TokenTypes#DO_WHILE DO_WHILE},
46   *  {@link TokenTypes#EQUAL EQUAL},
47   *  {@link TokenTypes#GE GE},
48   *  {@link TokenTypes#GT GT},
49   *  {@link TokenTypes#LAND LAND},
50   *  {@link TokenTypes#LCURLY LCURLY},
51   *  {@link TokenTypes#LE LE},
52   *  {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH},
53   *  {@link TokenTypes#LITERAL_DO LITERAL_DO},
54   *  {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE},
55   *  {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY},
56   *  {@link TokenTypes#LITERAL_FOR LITERAL_FOR},
57   *  {@link TokenTypes#LITERAL_IF LITERAL_IF},
58   *  {@link TokenTypes#LITERAL_RETURN LITERAL_RETURN},
59   *  {@link TokenTypes#LITERAL_SWITCH LITERAL_SWITCH},
60   *  {@link TokenTypes#LITERAL_SYNCHRONIZED LITERAL_SYNCHRONIZED},
61   *  {@link TokenTypes#LITERAL_TRY LITERAL_TRY},
62   *  {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE},
63   *  {@link TokenTypes#LOR LOR},
64   *  {@link TokenTypes#LT LT},
65   *  {@link TokenTypes#MINUS MINUS},
66   *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
67   *  {@link TokenTypes#MOD MOD},
68   *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
69   *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
70   *  {@link TokenTypes#PLUS PLUS},
71   *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
72   *  {@link TokenTypes#QUESTION QUESTION},
73   *  {@link TokenTypes#RCURLY RCURLY},
74   *  {@link TokenTypes#SL SL},
75   *  {@link TokenTypes#SLIST SLIST},
76   *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
77   *  {@link TokenTypes#SR SR},
78   *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
79   *  {@link TokenTypes#STAR STAR},
80   *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN},
81   *  {@link TokenTypes#LITERAL_ASSERT LITERAL_ASSERT},
82   *  {@link TokenTypes#TYPE_EXTENSION_AND TYPE_EXTENSION_AND}.
83   *
84   * <p>An example of how to configure the check is:
85   *
86   * <pre>
87   * &lt;module name="WhitespaceAround"/&gt;
88   * </pre>
89   *
90   * <p>An example of how to configure the check for whitespace only around
91   * assignment operators is:
92   *
93   * <pre>
94   * &lt;module name="WhitespaceAround"&gt;
95   *     &lt;property name="tokens"
96   *               value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,
97   *                      MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,
98   *                      BOR_ASSIGN,BAND_ASSIGN"/&gt;
99   * &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
168 public 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 }