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