001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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 *
037 * @since 3.0
038 */
039@StatelessCheck
040public class ParameterNumberCheck
041    extends AbstractCheck {
042
043    /**
044     * A key is pointing to the warning message text in "messages.properties"
045     * file.
046     */
047    public static final String MSG_KEY = "maxParam";
048
049    /** Default maximum number of allowed parameters. */
050    private static final int DEFAULT_MAX_PARAMETERS = 7;
051
052    /** Specify the maximum number of parameters allowed. */
053    private int max = DEFAULT_MAX_PARAMETERS;
054
055    /** Ignore number of parameters for methods with {@code @Override} annotation. */
056    private boolean ignoreOverriddenMethods;
057
058    /**
059     * Ignore methods and constructors annotated with the specified annotation(s).
060     */
061    private Set<String> ignoreAnnotatedBy = Collections.emptySet();
062
063    /**
064     * Setter to specify the maximum number of parameters allowed.
065     *
066     * @param max the max allowed parameters
067     * @since 3.0
068     */
069    public void setMax(int max) {
070        this.max = max;
071    }
072
073    /**
074     * Setter to ignore number of parameters for methods with {@code @Override} annotation.
075     *
076     * @param ignoreOverriddenMethods set ignore overridden methods
077     * @since 6.2
078     */
079    public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
080        this.ignoreOverriddenMethods = ignoreOverriddenMethods;
081    }
082
083    /**
084     * Setter to ignore methods and constructors annotated with the specified annotation(s).
085     *
086     * @param annotationNames specified annotation(s)
087     * @since 10.15.0
088     */
089    public void setIgnoreAnnotatedBy(String... annotationNames) {
090        ignoreAnnotatedBy = Set.of(annotationNames);
091    }
092
093    @Override
094    public int[] getDefaultTokens() {
095        return getAcceptableTokens();
096    }
097
098    @Override
099    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}