1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.naming;
21  
22  import java.util.Arrays;
23  import java.util.Optional;
24  
25  import com.puppycrawl.tools.checkstyle.api.DetailAST;
26  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
27  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
28  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
29  
30  /**
31   * <p>
32   * Checks that method parameter names conform to a format specified
33   * by the format property. By using {@code accessModifiers} property it is possible
34   * to specify different formats for methods at different visibility levels.
35   * </p>
36   * <p>
37   * To validate {@code catch} parameters please use
38   * <a href="#CatchParameterName">CatchParameterName</a>.
39   * </p>
40   * <p>
41   * To validate lambda parameters please use
42   * <a href="#LambdaParameterName">LambdaParameterName</a>.
43   * </p>
44   * <ul>
45   * <li>
46   * Property {@code format} - Specifies valid identifiers. Default value is
47   * {@code "^[a-z][a-zA-Z0-9]*$"}.
48   * </li>
49   * <li>
50   * Property {@code ignoreOverridden} - Allows to skip methods with Override annotation from
51   * validation. For example, the following method's parameter will be skipped from validation,
52   * if ignoreOverridden is true:
53   * <pre>
54   * &#64;Override
55   * public boolean equals(Object o) {
56   *   return super.equals(o);
57   * }
58   * </pre>
59   * Default value is {@code false}.
60   * </li>
61   * <li>
62   * Property {@code accessModifiers} - Access modifiers of methods where parameters are checked.
63   * Default value is {@code public, protected, package, private}.
64   * </li>
65   * </ul>
66   * <p>
67   * An example of how to configure the check:
68   * </p>
69   * <pre>
70   * &lt;module name="ParameterName"/&gt;
71   * </pre>
72   * <p>
73   * An example of how to configure the check for names that begin with
74   * a lower case letter, followed by letters, digits, and underscores:
75   * </p>
76   * <pre>
77   * &lt;module name="ParameterName"&gt;
78   *   &lt;property name="format" value="^[a-z][_a-zA-Z0-9]+$"/&gt;
79   * &lt;/module&gt;
80   * </pre>
81   * <p>
82   * An example of how to configure the check to skip methods with Override annotation from
83   * validation:
84   * </p>
85   * <pre>
86   * &lt;module name="ParameterName"&gt;
87   *   &lt;property name="ignoreOverridden" value="true"/&gt;
88   * &lt;/module&gt;
89   * </pre>
90   * <p>
91   * An example of how to configure the check for names that begin with a lower case letter, followed
92   * by letters and digits is:
93   * </p>
94   * <pre>
95   * &lt;module name="ParameterName"&gt;
96   *   &lt;property name="format" value="^[a-z][a-zA-Z0-9]+$"/&gt;
97   * &lt;/module&gt;
98   * </pre>
99   * <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  */
120 public 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 }