View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.google.common.collect.Lists;
8   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
9   import com.puppycrawl.tools.checkstyle.api.DetailAST;
10  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
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:
17   *
18   * <pre>
19   * String[] array = new String[] {
20   *      "first",
21   *      "second"
22   * };
23   * </pre>
24   *
25   * or
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.
34   *
35   * @since 0.6
36   */
37  public final class CurlyBracketsStructureCheck extends AbstractCheck {
38  
39      @Override
40      public int[] getDefaultTokens() {
41          return new int[] {
42              TokenTypes.ARRAY_INIT,
43          };
44      }
45  
46      @Override
47      public int[] getAcceptableTokens() {
48          return this.getDefaultTokens();
49      }
50  
51      @Override
52      public int[] getRequiredTokens() {
53          return this.getDefaultTokens();
54      }
55  
56      @Override
57      public void visitToken(final DetailAST ast) {
58          if (ast.getType() == TokenTypes.ARRAY_INIT) {
59              this.checkParams(ast);
60          }
61      }
62  
63      /**
64       * Checks params statement to satisfy the rule.
65       * @param node Tree node, containing containing array init statement.
66       */
67      private void checkParams(final DetailAST node) {
68          final DetailAST closing = node.findFirstToken(TokenTypes.RCURLY);
69          if (closing != null) {
70              this.checkLines(node, node.getLineNo(), closing.getLineNo());
71          }
72      }
73  
74      /**
75       * Checks params statement to satisfy the rule.
76       * @param node Tree node, containing array init statement.
77       * @param start First line
78       * @param end Final line
79       */
80      private void checkLines(final DetailAST node, final int start,
81          final int end) {
82          if (start != end) {
83              this.checkExpressions(
84                  CurlyBracketsStructureCheck.findAllChildren(
85                      node,
86                      TokenTypes.EXPR
87                  ),
88                  start,
89                  end
90              );
91          }
92      }
93  
94      /**
95       * Checks that all EXPR nodes satisfy the rule.
96       * @param exprs Iterable of EXPR nodes
97       * @param start First line of ARRAY_INIT node
98       * @param end Final line ARRAY_INIT node (corresponds to RCURLY)
99       */
100     private void checkExpressions(final Iterable<DetailAST> exprs,
101         final int start,
102         final int end
103     ) {
104         for (final DetailAST expr : exprs) {
105             final int pline = expr.getLineNo();
106             if (pline == start) {
107                 this.log(pline, "Parameters should start on a new line");
108             }
109             final DetailAST last = expr.getLastChild();
110             final int lline = last.getLineNo();
111             if (lline == end) {
112                 this.log(lline, "Closing bracket should be on a new line");
113             }
114         }
115     }
116 
117     /**
118      * Search for all children of given type.
119      * @param base Parent node to start from
120      * @param type Node type
121      * @return Iterable
122      */
123     private static Iterable<DetailAST> findAllChildren(final DetailAST base,
124         final int type) {
125         final List<DetailAST> children = Lists.newArrayList();
126         DetailAST child = base.getFirstChild();
127         while (child != null) {
128             if (child.getType() == type) {
129                 children.add(child);
130             }
131             child = child.getNextSibling();
132         }
133         return children;
134     }
135 }