001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.sizes;
021
022import java.util.Collections;
023import java.util.Set;
024
025import com.puppycrawl.tools.checkstyle.StatelessCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
030import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
031
032/**
033 * <div>
034 * Checks the number of parameters of a method or constructor.
035 * </div>
036 * <ul>
037 * <li>
038 * Property {@code ignoreAnnotatedBy} - Ignore methods and constructors
039 * annotated with the specified annotation(s).
040 * Type is {@code java.lang.String[]}.
041 * Default value is {@code ""}.
042 * </li>
043 * <li>
044 * Property {@code ignoreOverriddenMethods} - Ignore number of parameters for
045 * methods with {@code @Override} annotation.
046 * Type is {@code boolean}.
047 * Default value is {@code false}.
048 * </li>
049 * <li>
050 * Property {@code max} - Specify the maximum number of parameters allowed.
051 * Type is {@code int}.
052 * Default value is {@code 7}.
053 * </li>
054 * <li>
055 * Property {@code tokens} - tokens to check
056 * Type is {@code java.lang.String[]}.
057 * Validation type is {@code tokenSet}.
058 * Default value is:
059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
060 * METHOD_DEF</a>,
061 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
062 * CTOR_DEF</a>.
063 * </li>
064 * </ul>
065 *
066 * <p>
067 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
068 * </p>
069 *
070 * <p>
071 * Violation Message Keys:
072 * </p>
073 * <ul>
074 * <li>
075 * {@code maxParam}
076 * </li>
077 * </ul>
078 *
079 * @since 3.0
080 */
081@StatelessCheck
082public class ParameterNumberCheck
083    extends AbstractCheck {
084
085    /**
086     * A key is pointing to the warning message text in "messages.properties"
087     * file.
088     */
089    public static final String MSG_KEY = "maxParam";
090
091    /** Default maximum number of allowed parameters. */
092    private static final int DEFAULT_MAX_PARAMETERS = 7;
093
094    /** Specify the maximum number of parameters allowed. */
095    private int max = DEFAULT_MAX_PARAMETERS;
096
097    /** Ignore number of parameters for methods with {@code @Override} annotation. */
098    private boolean ignoreOverriddenMethods;
099
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}