View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8   import com.puppycrawl.tools.checkstyle.api.DetailAST;
9   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10  import java.util.regex.Pattern;
11  
12  /**
13   * Checks that a class, interface, enum or annotation declaration is followed
14   * by an empty line before its first member.
15   *
16   * <p>Some developers add an empty line after the opening brace of a
17   * class, interface or enum for aesthetic reasons, while others consider it
18   * wasted vertical space. This check enforces the presence of such a blank
19   * line so that the style is consistent across the whole codebase.
20   *
21   * <p>The following code will be reported as a violation because the first
22   * member is not preceded by an empty line:
23   * <pre>
24   * class Foo {
25   *     private int bar;
26   * }
27   * </pre>
28   *
29   * <p>Empty type bodies and one-line declarations are not reported.
30   *
31   * @since 0.24
32   */
33  public final class EmptyLineBeforeFirstMemberCheck extends AbstractCheck {
34  
35      /**
36       * Pattern matching an empty (whitespace-only) line.
37       */
38      private static final Pattern BLANK = Pattern.compile("^\\s*$");
39  
40      @Override
41      public int[] getDefaultTokens() {
42          return new int[] {
43              TokenTypes.CLASS_DEF,
44              TokenTypes.INTERFACE_DEF,
45              TokenTypes.ENUM_DEF,
46              TokenTypes.ANNOTATION_DEF,
47          };
48      }
49  
50      @Override
51      public int[] getAcceptableTokens() {
52          return this.getDefaultTokens();
53      }
54  
55      @Override
56      public int[] getRequiredTokens() {
57          return this.getDefaultTokens();
58      }
59  
60      @Override
61      public void visitToken(final DetailAST ast) {
62          final DetailAST block = ast.findFirstToken(TokenTypes.OBJBLOCK);
63          if (block != null) {
64              this.visitBlock(block);
65          }
66      }
67  
68      /**
69       * Check the body between curly braces of the given object block.
70       * @param block OBJBLOCK node whose body must be inspected
71       */
72      private void visitBlock(final DetailAST block) {
73          final DetailAST left = block.findFirstToken(TokenTypes.LCURLY);
74          final DetailAST right = block.findFirstToken(TokenTypes.RCURLY);
75          if (left != null && right != null
76              && right.getLineNo() - left.getLineNo() >= 2
77              && !EmptyLineBeforeFirstMemberCheck.BLANK
78                  .matcher(this.getLines()[left.getLineNo()]).find()) {
79              this.log(
80                  left.getLineNo() + 1,
81                  "Expected empty line before first member"
82              );
83          }
84      }
85  }