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