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=&quot;ParameterName&quot;/&gt;
071 * </pre>
072 * <p>Code Example:</p>
073 * <pre>
074 * class MyClass {
075 *   void method1(int v1) {} // OK
076 *   void method2(int V2) {} // violation, name 'V2' must match pattern '^[a-z][a-zA-Z0-9]*$'
077 * }
078 * </pre>
079 * <p>
080 * An example of how to configure the check for names that begin with
081 * a lower case letter, followed by letters, digits, and underscores:
082 * </p>
083 * <pre>
084 * &lt;module name=&quot;ParameterName&quot;&gt;
085 *   &lt;property name=&quot;format&quot; value=&quot;^[a-z][_a-zA-Z0-9]+$&quot;/&gt;
086 * &lt;/module&gt;
087 * </pre>
088 * <p>Code Example:</p>
089 * <pre>
090 * class MyClass {
091 *   void method1(int v1) {} // OK
092 *   void method2(int v_2) {} // OK
093 *   void method3(int V3) {} // violation, name 'V3' must match pattern '^[a-z][_a-zA-Z0-9]+$'
094 * }
095 * </pre>
096 * <p>
097 * An example of how to configure the check to skip methods with Override annotation from
098 * validation:
099 * </p>
100 * <pre>
101 * &lt;module name=&quot;ParameterName&quot;&gt;
102 *   &lt;property name=&quot;ignoreOverridden&quot; value=&quot;true&quot;/&gt;
103 * &lt;/module&gt;
104 * </pre>
105 * <p>Code Example:</p>
106 * <pre>
107 * class MyClass {
108 *   void method1(int v1) {} // OK
109 *   void method2(int V2) {} // violation, name 'V2' must match pattern '^[a-z][a-zA-Z0-9]*$'
110 *   &#064;Override
111 *   public boolean equals(Object V3) { // OK
112 *       return true;
113 *   }
114 * }
115 * </pre>
116 * <p>
117 * An example of how to configure the check for names that begin with a lower case letter, followed
118 * by letters and digits is:
119 * </p>
120 * <pre>
121 * &lt;module name=&quot;ParameterName&quot;&gt;
122 *   &lt;property name=&quot;format&quot; value=&quot;^[a-z][a-zA-Z0-9]+$&quot;/&gt;
123 * &lt;/module&gt;
124 * </pre>
125 * <p>Code Example:</p>
126 * <pre>
127 * class MyClass {
128 *   void method1(int v1) {} // OK
129 *   void method2(int v_2) {} // violation, name 'v_2' must match pattern '^[a-z][a-zA-Z0-9]+$'
130 *   void method3(int V3) {} // violation, name 'V3' must match pattern '^[a-z][a-zA-Z0-9]+$'
131 * }
132 * </pre>
133 * <p>
134 * The following configuration checks that the parameters always start with two lowercase
135 * characters and, in addition, that public method parameters cannot be one character long:
136 * </p>
137 * <pre>
138 * &lt;module name=&quot;ParameterName&quot;&gt;
139 *   &lt;property name=&quot;format&quot; value=&quot;^[a-z]([a-z0-9][a-zA-Z0-9]*)?$&quot;/&gt;
140 *   &lt;property name=&quot;accessModifiers&quot;
141 *     value=&quot;protected, package, private&quot;/&gt;
142 *   &lt;message key=&quot;name.invalidPattern&quot;
143 *     value=&quot;Parameter name ''{0}'' must match pattern ''{1}''&quot;/&gt;
144 * &lt;/module&gt;
145 * &lt;module name=&quot;ParameterName&quot;&gt;
146 *   &lt;property name=&quot;format&quot; value=&quot;^[a-z][a-z0-9][a-zA-Z0-9]*$&quot;/&gt;
147 *   &lt;property name=&quot;accessModifiers&quot; value=&quot;public&quot;/&gt;
148 *   &lt;message key=&quot;name.invalidPattern&quot;
149 *     value=&quot;Parameter name ''{0}'' must match pattern ''{1}''&quot;/&gt;
150 * &lt;/module&gt;
151 * </pre>
152 * <p>Code Example:</p>
153 * <pre>
154 * class MyClass {
155 *   void method1(int v1) {} // OK
156 *   protected method2(int V2) {} // violation, Parameter name 'V2'
157 *                                // must match pattern '^[a-z]([a-z0-9][a-zA-Z0-9]*)?$'
158 *   private method3(int a) {} // OK
159 *   public method4(int b) {} // violation, Parameter name 'b'
160 *                            // must match pattern '^[a-z][a-z0-9][a-zA-Z0-9]*$'
161 * }
162 * </pre>
163 *
164 * @since 3.0
165 */
166public class ParameterNameCheck extends AbstractNameCheck {
167
168    /**
169     * Allows to skip methods with Override annotation from validation. For example, the following
170     * method's parameter will be skipped from validation, if ignoreOverridden is true:
171     * <pre>
172     * &#64;Override
173     * public boolean equals(Object o) {
174     *   return super.equals(o);
175     * }
176     * </pre>
177     */
178    private boolean ignoreOverridden;
179
180    /** Access modifiers of methods where parameters are checked. */
181    private AccessModifier[] accessModifiers = {
182        AccessModifier.PUBLIC,
183        AccessModifier.PROTECTED,
184        AccessModifier.PACKAGE,
185        AccessModifier.PRIVATE,
186    };
187
188    /**
189     * Creates a new {@code ParameterNameCheck} instance.
190     */
191    public ParameterNameCheck() {
192        super("^[a-z][a-zA-Z0-9]*$");
193    }
194
195    /**
196     * Setter to allows to skip methods with Override annotation from validation. For example, the
197     * following method's parameter will be skipped from validation, if ignoreOverridden is true:
198     * <pre>
199     * &#64;Override
200     * public boolean equals(Object o) {
201     *   return super.equals(o);
202     * }
203     * </pre>
204     *
205     * @param ignoreOverridden Flag for skipping methods with Override annotation.
206     */
207    public void setIgnoreOverridden(boolean ignoreOverridden) {
208        this.ignoreOverridden = ignoreOverridden;
209    }
210
211    /**
212     * Setter to access modifiers of methods where parameters are checked.
213     *
214     * @param accessModifiers access modifiers of methods which should be checked.
215     */
216    public void setAccessModifiers(AccessModifier... accessModifiers) {
217        this.accessModifiers =
218            Arrays.copyOf(accessModifiers, accessModifiers.length);
219    }
220
221    @Override
222    public int[] getDefaultTokens() {
223        return getRequiredTokens();
224    }
225
226    @Override
227    public int[] getAcceptableTokens() {
228        return getRequiredTokens();
229    }
230
231    @Override
232    public int[] getRequiredTokens() {
233        return new int[] {TokenTypes.PARAMETER_DEF};
234    }
235
236    @Override
237    protected boolean mustCheckName(DetailAST ast) {
238        boolean checkName = true;
239        if (ignoreOverridden && isOverriddenMethod(ast)
240                || ast.getParent().getType() == TokenTypes.LITERAL_CATCH
241                || ast.getParent().getParent().getType() == TokenTypes.LAMBDA
242                || CheckUtil.isReceiverParameter(ast)
243                || !matchAccessModifiers(getAccessModifier(ast))) {
244            checkName = false;
245        }
246        return checkName;
247    }
248
249    /**
250     * Returns the access modifier of the method/constructor at the specified AST. If
251     * the method is in an interface or annotation block, the access modifier is assumed
252     * to be public.
253     *
254     * @param ast the token of the method/constructor.
255     * @return the access modifier of the method/constructor.
256     */
257    private static AccessModifier getAccessModifier(final DetailAST ast) {
258        final AccessModifier accessModifier;
259
260        if (ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
261            accessModifier = AccessModifier.PUBLIC;
262        }
263        else {
264            final DetailAST params = ast.getParent();
265            final DetailAST meth = params.getParent();
266            final DetailAST modsToken = meth.findFirstToken(TokenTypes.MODIFIERS);
267            accessModifier = CheckUtil.getAccessModifierFromModifiersToken(modsToken);
268        }
269
270        return accessModifier;
271    }
272
273    /**
274     * Checks whether a method has the correct access modifier to be checked.
275     *
276     * @param accessModifier the access modifier of the method.
277     * @return whether the method matches the expected access modifier.
278     */
279    private boolean matchAccessModifiers(final AccessModifier accessModifier) {
280        return Arrays.stream(accessModifiers).anyMatch(modifier -> modifier == accessModifier);
281    }
282
283    /**
284     * Checks whether a method is annotated with Override annotation.
285     *
286     * @param ast method parameter definition token.
287     * @return true if a method is annotated with Override annotation.
288     */
289    private static boolean isOverriddenMethod(DetailAST ast) {
290        boolean overridden = false;
291
292        final DetailAST parent = ast.getParent().getParent();
293        final Optional<DetailAST> annotation =
294            Optional.ofNullable(parent.getFirstChild().getFirstChild());
295
296        if (annotation.isPresent()) {
297            final Optional<DetailAST> overrideToken =
298                Optional.ofNullable(annotation.get().findFirstToken(TokenTypes.IDENT));
299            if (overrideToken.isPresent() && "Override".equals(overrideToken.get().getText())) {
300                overridden = true;
301            }
302        }
303        return overridden;
304    }
305
306}