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.ArrayList;
11  import java.util.List;
12  
13  /**
14   * Checks node/closing curly brackets to be the last symbols on the line.
15   *
16   * <p>This is how a correct curly bracket structure should look like:</p>
17   *
18   * <pre>
19   * String[] array = new String[] {
20   *      "first",
21   *      "second"
22   * };
23   * </pre>
24   *
25   * <p>or</p>
26   *
27   * <pre>
28   * String[] array = new String[] {"first", "second"};
29   * </pre>
30   *
31   * <p>The motivation for such formatting is simple - we want to see the entire
32   * block as fast as possible. When you look at a block of code you should be
33   * able to see where it starts and where it ends.</p>
34   *
35   * @since 0.6
36   */
37  public final class CurlyBracketsStructureCheck extends AbstractCheck {
38  
39      /**
40       * Default constructor.
41       */
42      public CurlyBracketsStructureCheck() {
43          // nothing to initialize
44      }
45  
46      @Override
47      public int[] getDefaultTokens() {
48          return new int[] {
49              TokenTypes.ARRAY_INIT,
50          };
51      }
52  
53      @Override
54      public int[] getAcceptableTokens() {
55          return this.getDefaultTokens();
56      }
57  
58      @Override
59      public int[] getRequiredTokens() {
60          return this.getDefaultTokens();
61      }
62  
63      @Override
64      public void visitToken(final DetailAST ast) {
65          if (ast.getType() == TokenTypes.ARRAY_INIT) {
66              this.checkParams(ast);
67          }
68      }
69  
70      private void checkParams(final DetailAST node) {
71          final DetailAST closing = node.findFirstToken(TokenTypes.RCURLY);
72          if (closing != null) {
73              this.checkLines(node, node.getLineNo(), closing.getLineNo());
74          }
75      }
76  
77      private void checkLines(final DetailAST node, final int start,
78          final int end) {
79          if (start != end) {
80              this.checkExpressions(
81                  CurlyBracketsStructureCheck.findAllChildren(
82                      node,
83                      TokenTypes.EXPR
84                  ),
85                  start,
86                  end
87              );
88          }
89      }
90  
91      private void checkExpressions(final Iterable<DetailAST> exprs,
92          final int start,
93          final int end
94      ) {
95          for (final DetailAST expr : exprs) {
96              final int pline = expr.getLineNo();
97              if (pline == start) {
98                  this.log(pline, "Parameters should start on a new line");
99              }
100             final DetailAST last = expr.getLastChild();
101             final int lline = last.getLineNo();
102             if (lline == end) {
103                 this.log(lline, "Closing bracket should be on a new line");
104             }
105         }
106     }
107 
108     private static Iterable<DetailAST> findAllChildren(final DetailAST base,
109         final int type) {
110         final List<DetailAST> children = new ArrayList<>(base.getChildCount());
111         DetailAST child = base.getFirstChild();
112         while (child != null) {
113             if (child.getType() == type) {
114                 children.add(child);
115             }
116             child = child.getNextSibling();
117         }
118         return children;
119     }
120 }