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