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 * <p>
034 * Checks the number of parameters of a method or constructor.
035 * </p>
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 * <p>
066 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
067 * </p>
068 * <p>
069 * Violation Message Keys:
070 * </p>
071 * <ul>
072 * <li>
073 * {@code maxParam}
074 * </li>
075 * </ul>
076 *
077 * @since 3.0
078 */
079@StatelessCheck
080public class ParameterNumberCheck
081    extends AbstractCheck {
082
083    /**
084     * A key is pointing to the warning message text in "messages.properties"
085     * file.
086     */
087    public static final String MSG_KEY = "maxParam";
088
089    /** Default maximum number of allowed parameters. */
090    private static final int DEFAULT_MAX_PARAMETERS = 7;
091
092    /** Specify the maximum number of parameters allowed. */
093    private int max = DEFAULT_MAX_PARAMETERS;
094
095    /** Ignore number of parameters for methods with {@code @Override} annotation. */
096    private boolean ignoreOverriddenMethods;
097
098    /**
099     * 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}