001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.AnnotationUtility;
027import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
028
029/**
030 * <p>
031 * Checks the number of parameters that a method or constructor has.
032 * The default allowable number of parameters is 7.
033 * To change the number of allowable parameters, set property max.
034 * Allows to ignore number of parameters for methods with
035 * &#064;{@link Override} annotation.
036 * </p>
037 * <p>
038 * An example of how to configure the check is:
039 * </p>
040 * <pre>
041 * &lt;module name="ParameterNumber"/&gt;
042 * </pre>
043 * <p>
044 * An example of how to configure the check to allow 10 parameters
045 * and ignoring parameters for methods with &#064;{@link Override}
046 * annotation is:
047 * </p>
048 * <pre>
049 * &lt;module name="ParameterNumber"&gt;
050 *    &lt;property name="max" value="10"/&gt;
051 *    &lt;property name="ignoreOverriddenMethods" value="true"/&gt;
052 * &lt;/module&gt;
053 * </pre>
054 * Java code that will be ignored:
055 * <pre>
056 * {@code
057 *
058 *  &#064;Override
059 *  public void needsLotsOfParameters(int a,
060 *      int b, int c, int d, int e, int f, int g, int h) {
061 *      ...
062 *  }
063 * }
064 * </pre>
065 * @author Oliver Burn
066 */
067@StatelessCheck
068public class ParameterNumberCheck
069    extends AbstractCheck {
070
071    /**
072     * A key is pointing to the warning message text in "messages.properties"
073     * file.
074     */
075    public static final String MSG_KEY = "maxParam";
076
077    /** {@link Override Override} annotation name. */
078    private static final String OVERRIDE = "Override";
079
080    /** Canonical {@link Override Override} annotation name. */
081    private static final String CANONICAL_OVERRIDE = "java.lang." + OVERRIDE;
082
083    /** Default maximum number of allowed parameters. */
084    private static final int DEFAULT_MAX_PARAMETERS = 7;
085
086    /** The maximum number of allowed parameters. */
087    private int max = DEFAULT_MAX_PARAMETERS;
088
089    /** Ignore overridden methods. */
090    private boolean ignoreOverriddenMethods;
091
092    /**
093     * Sets the maximum number of allowed parameters.
094     * @param max the max allowed parameters
095     */
096    public void setMax(int max) {
097        this.max = max;
098    }
099
100    /**
101     * Ignore number of parameters for methods with
102     * &#064;{@link Override} annotation.
103     * @param ignoreOverriddenMethods set ignore overridden methods
104     */
105    public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
106        this.ignoreOverriddenMethods = ignoreOverriddenMethods;
107    }
108
109    @Override
110    public int[] getDefaultTokens() {
111        return getAcceptableTokens();
112    }
113
114    @Override
115    public int[] getAcceptableTokens() {
116        return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF};
117    }
118
119    @Override
120    public int[] getRequiredTokens() {
121        return CommonUtils.EMPTY_INT_ARRAY;
122    }
123
124    @Override
125    public void visitToken(DetailAST ast) {
126        final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
127        final int count = params.getChildCount(TokenTypes.PARAMETER_DEF);
128        if (count > max && !shouldIgnoreNumberOfParameters(ast)) {
129            final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
130            log(name.getLineNo(), name.getColumnNo(), MSG_KEY, max, count);
131        }
132    }
133
134    /** Determine whether to ignore number of parameters for the method.
135     *
136     * @param ast the token to process
137     * @return true if this is overridden method and number of parameters should be ignored
138     *         false otherwise
139     */
140    private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
141        //if you override a method, you have no power over the number of parameters
142        return ignoreOverriddenMethods
143                && (AnnotationUtility.containsAnnotation(ast, OVERRIDE)
144                || AnnotationUtility.containsAnnotation(ast, CANONICAL_OVERRIDE));
145    }
146}