View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.sizes;
21  
22  import java.util.Collections;
23  import java.util.Set;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
30  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
31  
32  /**
33   * <div>
34   * Checks the number of parameters of a method or constructor.
35   * </div>
36   *
37   * @since 3.0
38   */
39  @StatelessCheck
40  public class ParameterNumberCheck
41      extends AbstractCheck {
42  
43      /**
44       * A key is pointing to the warning message text in "messages.properties"
45       * file.
46       */
47      public static final String MSG_KEY = "maxParam";
48  
49      /** Default maximum number of allowed parameters. */
50      private static final int DEFAULT_MAX_PARAMETERS = 7;
51  
52      /** Specify the maximum number of parameters allowed. */
53      private int max = DEFAULT_MAX_PARAMETERS;
54  
55      /** Ignore number of parameters for methods with {@code @Override} annotation. */
56      private boolean ignoreOverriddenMethods;
57  
58      /**
59       * Ignore methods and constructors annotated with the specified annotation(s).
60       */
61      private Set<String> ignoreAnnotatedBy = Collections.emptySet();
62  
63      /**
64       * Setter to specify the maximum number of parameters allowed.
65       *
66       * @param max the max allowed parameters
67       * @since 3.0
68       */
69      public void setMax(int max) {
70          this.max = max;
71      }
72  
73      /**
74       * Setter to ignore number of parameters for methods with {@code @Override} annotation.
75       *
76       * @param ignoreOverriddenMethods set ignore overridden methods
77       * @since 6.2
78       */
79      public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
80          this.ignoreOverriddenMethods = ignoreOverriddenMethods;
81      }
82  
83      /**
84       * Setter to ignore methods and constructors annotated with the specified annotation(s).
85       *
86       * @param annotationNames specified annotation(s)
87       * @since 10.15.0
88       */
89      public void setIgnoreAnnotatedBy(String... annotationNames) {
90          ignoreAnnotatedBy = Set.of(annotationNames);
91      }
92  
93      @Override
94      public int[] getDefaultTokens() {
95          return getAcceptableTokens();
96      }
97  
98      @Override
99      public int[] getAcceptableTokens() {
100         return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF};
101     }
102 
103     @Override
104     public int[] getRequiredTokens() {
105         return CommonUtil.EMPTY_INT_ARRAY;
106     }
107 
108     @Override
109     public void visitToken(DetailAST ast) {
110         final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
111         final int count = params.getChildCount(TokenTypes.PARAMETER_DEF);
112         if (count > max && !shouldIgnoreNumberOfParameters(ast)) {
113             final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
114             log(name, MSG_KEY, max, count);
115         }
116     }
117 
118     /**
119      * Determine whether to ignore number of parameters.
120      *
121      * @param ast the token to process
122      * @return true if number of parameters should be ignored.
123      */
124     private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
125         return isIgnoredOverriddenMethod(ast) || isAnnotatedByIgnoredAnnotations(ast);
126     }
127 
128     /**
129      * Checks if method is overridden and should be ignored.
130      *
131      * @param ast method definition to check
132      * @return true if method is overridden and should be ignored.
133      */
134     private boolean isIgnoredOverriddenMethod(DetailAST ast) {
135         return ignoreOverriddenMethods && AnnotationUtil.hasOverrideAnnotation(ast);
136     }
137 
138     /**
139      * Checks if method or constructor is annotated by ignored annotation(s).
140      *
141      * @param ast method or constructor definition to check
142      * @return true if annotated by ignored annotation(s).
143      */
144     private boolean isAnnotatedByIgnoredAnnotations(DetailAST ast) {
145         return AnnotationUtil.containsAnnotation(ast, ignoreAnnotatedBy);
146     }
147 
148 }