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.DetailAST;
8   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
9   
10  /**
11   * Number of parameters, tolerant to constructors that only take attributes
12   * and to private methods.
13   *
14   * <p>The stock {@code ParameterNumber} check treats a constructor as it
15   * treats a method, while a constructor has no choice: it must accept a
16   * value for every attribute of its class. A long list of parameters there
17   * is a symptom of a class with too many attributes, and the class is what
18   * has to be blamed, not its constructor. That's why a constructor with no
19   * more parameters than the number of attributes of its class passes here,
20   * no matter how big the limit is.</p>
21   *
22   * <p>A private method passes too. It is invisible outside its class, its
23   * parameters belong to no contract anybody else can see, and wrapping them
24   * into a new type only to satisfy the limit buys nothing.</p>
25   *
26   * <p>So does a method of a JNA binding, whose parameter list repeats the
27   * signature of a native function and cannot be shortened without breaking
28   * the mapping.</p>
29   *
30   * @since 1.0
31   */
32  public final class ParameterNumberCheck
33      extends com.puppycrawl.tools.checkstyle.checks.sizes.ParameterNumberCheck {
34  
35      /**
36       * Default constructor.
37       */
38      public ParameterNumberCheck() {
39          // nothing to initialize
40      }
41  
42      @Override
43      public void visitToken(final DetailAST ast) {
44          if (!ParameterNumberCheck.tolerated(ast)) {
45              super.visitToken(ast);
46          }
47      }
48  
49      private static boolean tolerated(final DetailAST ast) {
50          final boolean answer;
51          if (ast.getType() == TokenTypes.CTOR_DEF) {
52              answer = ParameterNumberCheck.parameters(ast)
53                  <= ParameterNumberCheck.attributes(ast);
54          } else {
55              answer = ast.findFirstToken(TokenTypes.MODIFIERS)
56                  .findFirstToken(TokenTypes.LITERAL_PRIVATE) != null
57                  || new JnaBinding(ast).is();
58          }
59          return answer;
60      }
61  
62      private static int parameters(final DetailAST ctor) {
63          return ctor.findFirstToken(TokenTypes.PARAMETERS)
64              .getChildCount(TokenTypes.PARAMETER_DEF);
65      }
66  
67      private static long attributes(final DetailAST ctor) {
68          final DetailAST block = ctor.getParent();
69          return ParameterNumberCheck.components(block.getParent())
70              + new ChildStream(block).children().filter(
71                  node -> node.getType() == TokenTypes.VARIABLE_DEF
72                      && node.findFirstToken(TokenTypes.MODIFIERS)
73                          .findFirstToken(TokenTypes.LITERAL_STATIC) == null
74              ).count();
75      }
76  
77      private static int components(final DetailAST type) {
78          final DetailAST components = type.findFirstToken(TokenTypes.RECORD_COMPONENTS);
79          final int count;
80          if (components == null) {
81              count = 0;
82          } else {
83              count = components.getChildCount(TokenTypes.RECORD_COMPONENT_DEF);
84          }
85          return count;
86      }
87  }