001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
027import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
028
029/**
030 * <p>
031 * Checks the number of parameters of a method or constructor.
032 * </p>
033 * <ul>
034 * <li>
035 * Property {@code max} - Specify the maximum number of parameters allowed.
036 * Type is {@code int}.
037 * Default value is {@code 7}.
038 * </li>
039 * <li>
040 * Property {@code ignoreOverriddenMethods} - Ignore number of parameters for
041 * methods with {@code @Override} annotation.
042 * Type is {@code boolean}.
043 * Default value is {@code false}.
044 * </li>
045 * <li>
046 * Property {@code tokens} - tokens to check
047 * Type is {@code int[]}.
048 * Default value is:
049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
050 * METHOD_DEF</a>,
051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
052 * CTOR_DEF</a>.
053 * </li>
054 * </ul>
055 * <p>
056 * To configure the check:
057 * </p>
058 * <pre>
059 * &lt;module name="ParameterNumber"/&gt;
060 * </pre>
061 * <p>
062 * To configure the check to allow 10 parameters for a method:
063 * </p>
064 * <pre>
065 * &lt;module name="ParameterNumber"&gt;
066 *   &lt;property name="max" value="10"/&gt;
067 *   &lt;property name="tokens" value="METHOD_DEF"/&gt;
068 * &lt;/module&gt;
069 * </pre>
070 * <p>
071 * To configure the check to ignore number of parameters for methods with
072 * {@code @Override} or {@code @java.lang.Override annotation}.
073 * </p>
074 * <p>
075 * Rationale: developer may need to override method with many parameters from
076 * 3-rd party library. In this case developer has no control over number of parameters.
077 * </p>
078 * <pre>
079 * &lt;module name="ParameterNumber"&gt;
080 *   &lt;property name="ignoreOverriddenMethods" value="true"/&gt;
081 * &lt;/module&gt;
082 * </pre>
083 * <p>
084 * Java code example:
085 * </p>
086 * <pre>
087 * &#064;Override
088 * public void needsLotsOfParameters(int a,
089 *     int b, int c, int d, int e, int f, int g, int h) {
090 *     ...
091 * }
092 * </pre>
093 * <p>
094 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
095 * </p>
096 * <p>
097 * Violation Message Keys:
098 * </p>
099 * <ul>
100 * <li>
101 * {@code maxParam}
102 * </li>
103 * </ul>
104 *
105 * @since 3.0
106 */
107@StatelessCheck
108public class ParameterNumberCheck
109    extends AbstractCheck {
110
111    /**
112     * A key is pointing to the warning message text in "messages.properties"
113     * file.
114     */
115    public static final String MSG_KEY = "maxParam";
116
117    /** {@link Override Override} annotation name. */
118    private static final String OVERRIDE = "Override";
119
120    /** Canonical {@link Override Override} annotation name. */
121    private static final String CANONICAL_OVERRIDE = "java.lang." + OVERRIDE;
122
123    /** Default maximum number of allowed parameters. */
124    private static final int DEFAULT_MAX_PARAMETERS = 7;
125
126    /** Specify the maximum number of parameters allowed. */
127    private int max = DEFAULT_MAX_PARAMETERS;
128
129    /** Ignore number of parameters for methods with {@code @Override} annotation. */
130    private boolean ignoreOverriddenMethods;
131
132    /**
133     * Setter to specify the maximum number of parameters allowed.
134     *
135     * @param max the max allowed parameters
136     */
137    public void setMax(int max) {
138        this.max = max;
139    }
140
141    /**
142     * Setter to ignore number of parameters for methods with {@code @Override} annotation.
143     *
144     * @param ignoreOverriddenMethods set ignore overridden methods
145     */
146    public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
147        this.ignoreOverriddenMethods = ignoreOverriddenMethods;
148    }
149
150    @Override
151    public int[] getDefaultTokens() {
152        return getAcceptableTokens();
153    }
154
155    @Override
156    public int[] getAcceptableTokens() {
157        return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF};
158    }
159
160    @Override
161    public int[] getRequiredTokens() {
162        return CommonUtil.EMPTY_INT_ARRAY;
163    }
164
165    @Override
166    public void visitToken(DetailAST ast) {
167        final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
168        final int count = params.getChildCount(TokenTypes.PARAMETER_DEF);
169        if (count > max && !shouldIgnoreNumberOfParameters(ast)) {
170            final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
171            log(name, MSG_KEY, max, count);
172        }
173    }
174
175    /**
176     * Determine whether to ignore number of parameters for the method.
177     *
178     * @param ast the token to process
179     * @return true if this is overridden method and number of parameters should be ignored
180     *         false otherwise
181     */
182    private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
183        // if you override a method, you have no power over the number of parameters
184        return ignoreOverriddenMethods
185                && (AnnotationUtil.containsAnnotation(ast, OVERRIDE)
186                || AnnotationUtil.containsAnnotation(ast, CANONICAL_OVERRIDE));
187    }
188
189}