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 the order of constructor declarations.
15   *
16   * <p>A primary constructor is the one that does the real initialization
17   * work and does not delegate to another constructor via {@code this(...)}.
18   * A secondary constructor delegates, as its first statement, to another
19   * constructor in the same class. The rule requires the primary constructor
20   * to be declared after all secondary ones, so that the delegation chain
21   * reads top-down towards the primary.</p>
22   *
23   * @since 0.24
24   */
25  public final class ConstructorsOrderCheck extends AbstractCheck {
26  
27      /**
28       * Default constructor.
29       */
30      public ConstructorsOrderCheck() {
31          // nothing to initialize
32      }
33  
34      @Override
35      public int[] getDefaultTokens() {
36          return new int[] {
37              TokenTypes.CLASS_DEF,
38              TokenTypes.ENUM_DEF,
39              TokenTypes.RECORD_DEF,
40          };
41      }
42  
43      @Override
44      public int[] getAcceptableTokens() {
45          return this.getDefaultTokens();
46      }
47  
48      @Override
49      public int[] getRequiredTokens() {
50          return this.getDefaultTokens();
51      }
52  
53      @Override
54      public void visitToken(final DetailAST ast) {
55          final DetailAST obj = ast.findFirstToken(TokenTypes.OBJBLOCK);
56          if (obj != null) {
57              this.checkOrder(obj);
58          }
59      }
60  
61      private void checkOrder(final DetailAST obj) {
62          boolean primary = false;
63          for (final DetailAST ctor : ConstructorsOrderCheck.constructors(obj)) {
64              if (primary) {
65                  this.log(
66                      ctor.getLineNo(),
67                      "Primary constructor must be the last one declared"
68                  );
69              } else if (!ConstructorsOrderCheck.delegates(ctor)) {
70                  primary = true;
71              }
72          }
73      }
74  
75      private static List<DetailAST> constructors(final DetailAST obj) {
76          final List<DetailAST> ctors = new ArrayList<>(0);
77          for (DetailAST child = obj.getFirstChild();
78              child != null; child = child.getNextSibling()) {
79              if (child.getType() == TokenTypes.CTOR_DEF) {
80                  ctors.add(child);
81              }
82          }
83          return ctors;
84      }
85  
86      private static boolean delegates(final DetailAST ctor) {
87          boolean delegates = false;
88          final DetailAST body = ctor.findFirstToken(TokenTypes.SLIST);
89          if (body != null) {
90              for (DetailAST stmt = body.getFirstChild();
91                  stmt != null; stmt = stmt.getNextSibling()) {
92                  if (stmt.getType() == TokenTypes.CTOR_CALL) {
93                      delegates = true;
94                      break;
95                  }
96              }
97          }
98          return delegates;
99      }
100 }