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