001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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;
028
029/**
030 * <div>
031 * Checks that method parameter names conform to a specified pattern.
032 * By using {@code accessModifiers} property it is possible
033 * to specify different formats for methods at different visibility levels.
034 * </div>
035 *
036 * <p>
037 * To validate {@code catch} parameters please use
038 * <a href="https://checkstyle.org/checks/naming/catchparametername.html#CatchParameterName">
039 * CatchParameterName</a>.
040 * </p>
041 *
042 * <p>
043 * To validate lambda parameters please use
044 * <a href="https://checkstyle.org/checks/naming/lambdaparametername.html#LambdaParameterName">
045 * LambdaParameterName</a>.
046 * </p>
047 * <ul>
048 * <li>
049 * Property {@code accessModifiers} - Access modifiers of methods where parameters are
050 * checked.
051 * Type is {@code com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption[]}.
052 * Default value is {@code public, protected, package, private}.
053 * </li>
054 * <li>
055 * Property {@code format} - Sets the pattern to match valid identifiers.
056 * Type is {@code java.util.regex.Pattern}.
057 * Default value is {@code "^[a-z][a-zA-Z0-9]*$"}.
058 * </li>
059 * <li>
060 * Property {@code ignoreOverridden} - Allows to skip methods with Override annotation from
061 * validation.
062 * Type is {@code boolean}.
063 * Default value is {@code false}.
064 * </li>
065 * </ul>
066 *
067 * <p>
068 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
069 * </p>
070 *
071 * <p>
072 * Violation Message Keys:
073 * </p>
074 * <ul>
075 * <li>
076 * {@code name.invalidPattern}
077 * </li>
078 * </ul>
079 *
080 * @since 3.0
081 */
082public class ParameterNameCheck extends AbstractNameCheck {
083
084    /**
085     * Allows to skip methods with Override annotation from validation.
086     */
087    private boolean ignoreOverridden;
088
089    /** Access modifiers of methods where parameters are checked. */
090    private AccessModifierOption[] accessModifiers = {
091        AccessModifierOption.PUBLIC,
092        AccessModifierOption.PROTECTED,
093        AccessModifierOption.PACKAGE,
094        AccessModifierOption.PRIVATE,
095    };
096
097    /**
098     * Creates a new {@code ParameterNameCheck} instance.
099     */
100    public ParameterNameCheck() {
101        super("^[a-z][a-zA-Z0-9]*$");
102    }
103
104    /**
105     * Setter to allows to skip methods with Override annotation from validation.
106     *
107     * @param ignoreOverridden Flag for skipping methods with Override annotation.
108     * @since 6.12.1
109     */
110    public void setIgnoreOverridden(boolean ignoreOverridden) {
111        this.ignoreOverridden = ignoreOverridden;
112    }
113
114    /**
115     * Setter to access modifiers of methods where parameters are checked.
116     *
117     * @param accessModifiers access modifiers of methods which should be checked.
118     * @since 7.5
119     */
120    public void setAccessModifiers(AccessModifierOption... accessModifiers) {
121        this.accessModifiers =
122            Arrays.copyOf(accessModifiers, accessModifiers.length);
123    }
124
125    @Override
126    public int[] getDefaultTokens() {
127        return getRequiredTokens();
128    }
129
130    @Override
131    public int[] getAcceptableTokens() {
132        return getRequiredTokens();
133    }
134
135    @Override
136    public int[] getRequiredTokens() {
137        return new int[] {TokenTypes.PARAMETER_DEF};
138    }
139
140    @Override
141    protected boolean mustCheckName(DetailAST ast) {
142        boolean checkName = true;
143        final DetailAST parent = ast.getParent();
144        if (ignoreOverridden && isOverriddenMethod(ast)
145                || parent.getType() == TokenTypes.LITERAL_CATCH
146                || parent.getParent().getType() == TokenTypes.LAMBDA
147                || CheckUtil.isReceiverParameter(ast)
148                || !matchAccessModifiers(
149                        CheckUtil.getAccessModifierFromModifiersToken(parent.getParent()))) {
150            checkName = false;
151        }
152        return checkName;
153    }
154
155    /**
156     * Checks whether a method has the correct access modifier to be checked.
157     *
158     * @param accessModifier the access modifier of the method.
159     * @return whether the method matches the expected access modifier.
160     */
161    private boolean matchAccessModifiers(final AccessModifierOption accessModifier) {
162        return Arrays.stream(accessModifiers)
163                .anyMatch(modifier -> modifier == accessModifier);
164    }
165
166    /**
167     * Checks whether a method is annotated with Override annotation.
168     *
169     * @param ast method parameter definition token.
170     * @return true if a method is annotated with Override annotation.
171     */
172    private static boolean isOverriddenMethod(DetailAST ast) {
173        boolean overridden = false;
174
175        final DetailAST parent = ast.getParent().getParent();
176        final Optional<DetailAST> annotation =
177            Optional.ofNullable(parent.getFirstChild().getFirstChild());
178
179        if (annotation.isPresent()) {
180            final Optional<DetailAST> overrideToken =
181                Optional.ofNullable(annotation.orElseThrow().findFirstToken(TokenTypes.IDENT));
182            if (overrideToken.isPresent()
183                && "Override".equals(overrideToken.orElseThrow().getText())) {
184                overridden = true;
185            }
186        }
187        return overridden;
188    }
189
190}