1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 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 * <ul>
37 * <li>
38 * Property {@code ignoreAnnotatedBy} - Ignore methods and constructors
39 * annotated with the specified annotation(s).
40 * Type is {@code java.lang.String[]}.
41 * Default value is {@code ""}.
42 * </li>
43 * <li>
44 * Property {@code ignoreOverriddenMethods} - Ignore number of parameters for
45 * methods with {@code @Override} annotation.
46 * Type is {@code boolean}.
47 * Default value is {@code false}.
48 * </li>
49 * <li>
50 * Property {@code max} - Specify the maximum number of parameters allowed.
51 * Type is {@code int}.
52 * Default value is {@code 7}.
53 * </li>
54 * <li>
55 * Property {@code tokens} - tokens to check
56 * Type is {@code java.lang.String[]}.
57 * Validation type is {@code tokenSet}.
58 * Default value is:
59 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
60 * METHOD_DEF</a>,
61 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
62 * CTOR_DEF</a>.
63 * </li>
64 * </ul>
65 *
66 * <p>
67 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
68 * </p>
69 *
70 * <p>
71 * Violation Message Keys:
72 * </p>
73 * <ul>
74 * <li>
75 * {@code maxParam}
76 * </li>
77 * </ul>
78 *
79 * @since 3.0
80 */
81 @StatelessCheck
82 public class ParameterNumberCheck
83 extends AbstractCheck {
84
85 /**
86 * A key is pointing to the warning message text in "messages.properties"
87 * file.
88 */
89 public static final String MSG_KEY = "maxParam";
90
91 /** Default maximum number of allowed parameters. */
92 private static final int DEFAULT_MAX_PARAMETERS = 7;
93
94 /** Specify the maximum number of parameters allowed. */
95 private int max = DEFAULT_MAX_PARAMETERS;
96
97 /** Ignore number of parameters for methods with {@code @Override} annotation. */
98 private boolean ignoreOverriddenMethods;
99
100 /**
101 * Ignore methods and constructors annotated with the specified annotation(s).
102 */
103 private Set<String> ignoreAnnotatedBy = Collections.emptySet();
104
105 /**
106 * Setter to specify the maximum number of parameters allowed.
107 *
108 * @param max the max allowed parameters
109 * @since 3.0
110 */
111 public void setMax(int max) {
112 this.max = max;
113 }
114
115 /**
116 * Setter to ignore number of parameters for methods with {@code @Override} annotation.
117 *
118 * @param ignoreOverriddenMethods set ignore overridden methods
119 * @since 6.2
120 */
121 public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
122 this.ignoreOverriddenMethods = ignoreOverriddenMethods;
123 }
124
125 /**
126 * Setter to ignore methods and constructors annotated with the specified annotation(s).
127 *
128 * @param annotationNames specified annotation(s)
129 * @since 10.15.0
130 */
131 public void setIgnoreAnnotatedBy(String... annotationNames) {
132 ignoreAnnotatedBy = Set.of(annotationNames);
133 }
134
135 @Override
136 public int[] getDefaultTokens() {
137 return getAcceptableTokens();
138 }
139
140 @Override
141 public int[] getAcceptableTokens() {
142 return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF};
143 }
144
145 @Override
146 public int[] getRequiredTokens() {
147 return CommonUtil.EMPTY_INT_ARRAY;
148 }
149
150 @Override
151 public void visitToken(DetailAST ast) {
152 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
153 final int count = params.getChildCount(TokenTypes.PARAMETER_DEF);
154 if (count > max && !shouldIgnoreNumberOfParameters(ast)) {
155 final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
156 log(name, MSG_KEY, max, count);
157 }
158 }
159
160 /**
161 * Determine whether to ignore number of parameters.
162 *
163 * @param ast the token to process
164 * @return true if number of parameters should be ignored.
165 */
166 private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
167 return isIgnoredOverriddenMethod(ast) || isAnnotatedByIgnoredAnnotations(ast);
168 }
169
170 /**
171 * Checks if method is overridden and should be ignored.
172 *
173 * @param ast method definition to check
174 * @return true if method is overridden and should be ignored.
175 */
176 private boolean isIgnoredOverriddenMethod(DetailAST ast) {
177 return ignoreOverriddenMethods && AnnotationUtil.hasOverrideAnnotation(ast);
178 }
179
180 /**
181 * Checks if method or constructor is annotated by ignored annotation(s).
182 *
183 * @param ast method or constructor definition to check
184 * @return true if annotated by ignored annotation(s).
185 */
186 private boolean isAnnotatedByIgnoredAnnotations(DetailAST ast) {
187 return AnnotationUtil.containsAnnotation(ast, ignoreAnnotatedBy);
188 }
189
190 }