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.naming;
021
022import java.util.Arrays;
023import java.util.Optional;
024
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
028import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
029
030/**
031 * <p>
032 * Checks that method parameter names conform to a specified pattern.
033 * By using {@code accessModifiers} property it is possible
034 * to specify different formats for methods at different visibility levels.
035 * </p>
036 * <p>
037 * To validate {@code catch} parameters please use
038 * <a href="https://checkstyle.org/config_naming.html#CatchParameterName">CatchParameterName</a>.
039 * </p>
040 * <p>
041 * To validate lambda parameters please use
042 * <a href="https://checkstyle.org/config_naming.html#LambdaParameterName">LambdaParameterName</a>.
043 * </p>
044 * <ul>
045 * <li>
046 * Property {@code format} - Specifies valid identifiers. Default value is
047 * {@code "^[a-z][a-zA-Z0-9]*$"}.
048 * </li>
049 * <li>
050 * Property {@code ignoreOverridden} - Allows to skip methods with Override annotation from
051 * validation. For example, the following method's parameter will be skipped from validation,
052 * if ignoreOverridden is true:
053 * <pre>
054 * &#64;Override
055 * public boolean equals(Object o) {
056 *   return super.equals(o);
057 * }
058 * </pre>
059 * Default value is {@code false}.
060 * </li>
061 * <li>
062 * Property {@code accessModifiers} - Access modifiers of methods where parameters are checked.
063 * Default value is {@code public, protected, package, private}.
064 * </li>
065 * </ul>
066 * <p>
067 * An example of how to configure the check:
068 * </p>
069 * <pre>
070 * &lt;module name="ParameterName"/&gt;
071 * </pre>
072 * <p>
073 * An example of how to configure the check for names that begin with
074 * a lower case letter, followed by letters, digits, and underscores:
075 * </p>
076 * <pre>
077 * &lt;module name="ParameterName"&gt;
078 *   &lt;property name="format" value="^[a-z][_a-zA-Z0-9]+$"/&gt;
079 * &lt;/module&gt;
080 * </pre>
081 * <p>
082 * An example of how to configure the check to skip methods with Override annotation from
083 * validation:
084 * </p>
085 * <pre>
086 * &lt;module name="ParameterName"&gt;
087 *   &lt;property name="ignoreOverridden" value="true"/&gt;
088 * &lt;/module&gt;
089 * </pre>
090 * <p>
091 * An example of how to configure the check for names that begin with a lower case letter, followed
092 * by letters and digits is:
093 * </p>
094 * <pre>
095 * &lt;module name="ParameterName"&gt;
096 *   &lt;property name="format" value="^[a-z][a-zA-Z0-9]+$"/&gt;
097 * &lt;/module&gt;
098 * </pre>
099 * <p>
100 * The following configuration checks that the parameters always start with two lowercase
101 * characters and, in addition, that public method parameters cannot be one character long:
102 * </p>
103 * <pre>
104 * &lt;module name="ParameterName"&gt;
105 *   &lt;property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/&gt;
106 *   &lt;property name="accessModifiers" value="protected, package, private"/&gt;
107 *   &lt;message key="name.invalidPattern"
108 *     value="Parameter name ''{0}'' must match pattern ''{1}''"/&gt;
109 * &lt;/module&gt;
110 * &lt;module name="ParameterName"&gt;
111 *   &lt;property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/&gt;
112 *   &lt;property name="accessModifiers" value="public"/&gt;
113 *   &lt;message key="name.invalidPattern"
114 *     value="Parameter name ''{0}'' must match pattern ''{1}''"/&gt;
115 * &lt;/module&gt;
116 * </pre>
117 *
118 * @since 3.0
119 */
120public class ParameterNameCheck extends AbstractNameCheck {
121
122    /**
123     * Allows to skip methods with Override annotation from validation. For example, the following
124     * method's parameter will be skipped from validation, if ignoreOverridden is true:
125     * <pre>
126     * &#64;Override
127     * public boolean equals(Object o) {
128     *   return super.equals(o);
129     * }
130     * </pre>
131     */
132    private boolean ignoreOverridden;
133
134    /** Access modifiers of methods where parameters are checked. */
135    private AccessModifier[] accessModifiers = {
136        AccessModifier.PUBLIC,
137        AccessModifier.PROTECTED,
138        AccessModifier.PACKAGE,
139        AccessModifier.PRIVATE,
140    };
141
142    /**
143     * Creates a new {@code ParameterNameCheck} instance.
144     */
145    public ParameterNameCheck() {
146        super("^[a-z][a-zA-Z0-9]*$");
147    }
148
149    /**
150     * Setter to allows to skip methods with Override annotation from validation. For example, the
151     * following method's parameter will be skipped from validation, if ignoreOverridden is true:
152     * <pre>
153     * &#64;Override
154     * public boolean equals(Object o) {
155     *   return super.equals(o);
156     * }
157     * </pre>
158     * @param ignoreOverridden Flag for skipping methods with Override annotation.
159     */
160    public void setIgnoreOverridden(boolean ignoreOverridden) {
161        this.ignoreOverridden = ignoreOverridden;
162    }
163
164    /**
165     * Setter to access modifiers of methods where parameters are checked.
166     * @param accessModifiers access modifiers of methods which should be checked.
167     */
168    public void setAccessModifiers(AccessModifier... accessModifiers) {
169        this.accessModifiers =
170            Arrays.copyOf(accessModifiers, accessModifiers.length);
171    }
172
173    @Override
174    public int[] getDefaultTokens() {
175        return getRequiredTokens();
176    }
177
178    @Override
179    public int[] getAcceptableTokens() {
180        return getRequiredTokens();
181    }
182
183    @Override
184    public int[] getRequiredTokens() {
185        return new int[] {TokenTypes.PARAMETER_DEF};
186    }
187
188    @Override
189    protected boolean mustCheckName(DetailAST ast) {
190        boolean checkName = true;
191        if (ignoreOverridden && isOverriddenMethod(ast)
192                || ast.getParent().getType() == TokenTypes.LITERAL_CATCH
193                || ast.getParent().getParent().getType() == TokenTypes.LAMBDA
194                || CheckUtil.isReceiverParameter(ast)
195                || !matchAccessModifiers(getAccessModifier(ast))) {
196            checkName = false;
197        }
198        return checkName;
199    }
200
201    /**
202     * Returns the access modifier of the method/constructor at the specified AST. If
203     * the method is in an interface or annotation block, the access modifier is assumed
204     * to be public.
205     *
206     * @param ast the token of the method/constructor.
207     * @return the access modifier of the method/constructor.
208     */
209    private static AccessModifier getAccessModifier(final DetailAST ast) {
210        final AccessModifier accessModifier;
211
212        if (ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
213            accessModifier = AccessModifier.PUBLIC;
214        }
215        else {
216            final DetailAST params = ast.getParent();
217            final DetailAST meth = params.getParent();
218            final DetailAST modsToken = meth.findFirstToken(TokenTypes.MODIFIERS);
219            accessModifier = CheckUtil.getAccessModifierFromModifiersToken(modsToken);
220        }
221
222        return accessModifier;
223    }
224
225    /**
226     * Checks whether a method has the correct access modifier to be checked.
227     * @param accessModifier the access modifier of the method.
228     * @return whether the method matches the expected access modifier.
229     */
230    private boolean matchAccessModifiers(final AccessModifier accessModifier) {
231        return Arrays.stream(accessModifiers).anyMatch(modifier -> modifier == accessModifier);
232    }
233
234    /**
235     * Checks whether a method is annotated with Override annotation.
236     * @param ast method parameter definition token.
237     * @return true if a method is annotated with Override annotation.
238     */
239    private static boolean isOverriddenMethod(DetailAST ast) {
240        boolean overridden = false;
241
242        final DetailAST parent = ast.getParent().getParent();
243        final Optional<DetailAST> annotation =
244            Optional.ofNullable(parent.getFirstChild().getFirstChild());
245
246        if (annotation.isPresent()) {
247            final Optional<DetailAST> overrideToken =
248                Optional.ofNullable(annotation.get().findFirstToken(TokenTypes.IDENT));
249            if (overrideToken.isPresent() && "Override".equals(overrideToken.get().getText())) {
250                overridden = true;
251            }
252        }
253        return overridden;
254    }
255
256}