001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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">
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">
045 * LambdaParameterName</a>.
046 * </p>
047 *
048 * @since 3.0
049 */
050public class ParameterNameCheck extends AbstractNameCheck {
051
052    /**
053     * Allows to skip methods with Override annotation from validation.
054     */
055    private boolean ignoreOverridden;
056
057    /** Access modifiers of methods where parameters are checked. */
058    private AccessModifierOption[] accessModifiers = {
059        AccessModifierOption.PUBLIC,
060        AccessModifierOption.PROTECTED,
061        AccessModifierOption.PACKAGE,
062        AccessModifierOption.PRIVATE,
063    };
064
065    /**
066     * Creates a new {@code ParameterNameCheck} instance.
067     */
068    public ParameterNameCheck() {
069        super("^[a-z][a-zA-Z0-9]*$");
070    }
071
072    /**
073     * Setter to allows to skip methods with Override annotation from validation.
074     *
075     * @param ignoreOverridden Flag for skipping methods with Override annotation.
076     * @since 6.12.1
077     */
078    public void setIgnoreOverridden(boolean ignoreOverridden) {
079        this.ignoreOverridden = ignoreOverridden;
080    }
081
082    /**
083     * Setter to access modifiers of methods where parameters are checked.
084     *
085     * @param accessModifiers access modifiers of methods which should be checked.
086     * @since 7.5
087     */
088    public void setAccessModifiers(AccessModifierOption... accessModifiers) {
089        this.accessModifiers =
090            Arrays.copyOf(accessModifiers, accessModifiers.length);
091    }
092
093    @Override
094    public int[] getDefaultTokens() {
095        return getRequiredTokens();
096    }
097
098    @Override
099    public int[] getAcceptableTokens() {
100        return getRequiredTokens();
101    }
102
103    @Override
104    public int[] getRequiredTokens() {
105        return new int[] {TokenTypes.PARAMETER_DEF};
106    }
107
108    @Override
109    protected boolean mustCheckName(DetailAST ast) {
110        boolean checkName = true;
111        final DetailAST parent = ast.getParent();
112        if (ignoreOverridden && isOverriddenMethod(ast)
113                || parent.getType() == TokenTypes.LITERAL_CATCH
114                || parent.getParent().getType() == TokenTypes.LAMBDA
115                || CheckUtil.isReceiverParameter(ast)
116                || !matchAccessModifiers(
117                        CheckUtil.getAccessModifierFromModifiersToken(parent.getParent()))) {
118            checkName = false;
119        }
120        return checkName;
121    }
122
123    /**
124     * Checks whether a method has the correct access modifier to be checked.
125     *
126     * @param accessModifier the access modifier of the method.
127     * @return whether the method matches the expected access modifier.
128     */
129    private boolean matchAccessModifiers(final AccessModifierOption accessModifier) {
130        return Arrays.stream(accessModifiers)
131                .anyMatch(modifier -> modifier == accessModifier);
132    }
133
134    /**
135     * Checks whether a method is annotated with Override annotation.
136     *
137     * @param ast method parameter definition token.
138     * @return true if a method is annotated with Override annotation.
139     */
140    private static boolean isOverriddenMethod(DetailAST ast) {
141        boolean overridden = false;
142
143        final DetailAST parent = ast.getParent().getParent();
144        final Optional<DetailAST> annotation =
145            Optional.ofNullable(parent.getFirstChild().getFirstChild());
146
147        if (annotation.isPresent()) {
148            final Optional<DetailAST> overrideToken =
149                Optional.ofNullable(annotation.orElseThrow().findFirstToken(TokenTypes.IDENT));
150            if (overrideToken.isPresent()
151                && "Override".equals(overrideToken.orElseThrow().getText())) {
152                overridden = true;
153            }
154        }
155        return overridden;
156    }
157
158}