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