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 }