View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.naming;
21  
22  import java.util.Arrays;
23  import java.util.Optional;
24  
25  import com.puppycrawl.tools.checkstyle.api.DetailAST;
26  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
27  import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
28  import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
29  
30  /**
31   * <p>
32   * Checks that method and {@code catch} parameter names conform to a format specified
33   * by the format property. The format is a
34   * {@link java.util.regex.Pattern regular expression}
35   * and defaults to
36   * <strong>^[a-z][a-zA-Z0-9]*$</strong>.
37   * </p>
38   * <p>The check has the following options:</p>
39   * <p><b>ignoreOverridden</b> - allows to skip methods with Override annotation from
40   * validation. Default values is <b>false</b> .</p>
41   * <p><b>accessModifiers</b> - access modifiers of methods which should to be checked.
42   * Default value is <b>public, protected, package, private</b> .</p>
43   * An example of how to configure the check:
44   * <pre>
45   * &lt;module name="ParameterName"/&gt;
46   * </pre>
47   * <p>
48   * An example of how to configure the check for names that begin with
49   * a lower case letter, followed by letters, digits, and underscores:
50   * </p>
51   * <pre>
52   * &lt;module name="ParameterName"&gt;
53   *    &lt;property name="format" value="^[a-z][_a-zA-Z0-9]+$"/&gt;
54   * &lt;/module&gt;
55   * </pre>
56   * <p>
57   * An example of how to configure the check to skip methods with Override annotation from
58   * validation:
59   * </p>
60   * <pre>
61   * &lt;module name="ParameterName"&gt;
62   *    &lt;property name="ignoreOverridden" value="true"/&gt;
63   * &lt;/module&gt;
64   * </pre>
65   *
66   * @author Oliver Burn
67   * @author Andrei Selkin
68   */
69  public class ParameterNameCheck extends AbstractNameCheck {
70  
71      /**
72       * Allows to skip methods with Override annotation from validation.
73       */
74      private boolean ignoreOverridden;
75  
76      /** Access modifiers of methods which should be checked. */
77      private AccessModifier[] accessModifiers = {
78          AccessModifier.PUBLIC,
79          AccessModifier.PROTECTED,
80          AccessModifier.PACKAGE,
81          AccessModifier.PRIVATE,
82      };
83  
84      /**
85       * Creates a new {@code ParameterNameCheck} instance.
86       */
87      public ParameterNameCheck() {
88          super("^[a-z][a-zA-Z0-9]*$");
89      }
90  
91      /**
92       * Sets whether to skip methods with Override annotation from validation.
93       * @param ignoreOverridden Flag for skipping methods with Override annotation.
94       */
95      public void setIgnoreOverridden(boolean ignoreOverridden) {
96          this.ignoreOverridden = ignoreOverridden;
97      }
98  
99      /**
100      * Sets access modifiers of methods which should be checked.
101      * @param accessModifiers access modifiers of methods which should be checked.
102      */
103     public void setAccessModifiers(AccessModifier... accessModifiers) {
104         this.accessModifiers =
105             Arrays.copyOf(accessModifiers, accessModifiers.length);
106     }
107 
108     @Override
109     public int[] getDefaultTokens() {
110         return getRequiredTokens();
111     }
112 
113     @Override
114     public int[] getAcceptableTokens() {
115         return getRequiredTokens();
116     }
117 
118     @Override
119     public int[] getRequiredTokens() {
120         return new int[] {TokenTypes.PARAMETER_DEF};
121     }
122 
123     @Override
124     protected boolean mustCheckName(DetailAST ast) {
125         boolean checkName = true;
126         if (ignoreOverridden && isOverriddenMethod(ast)
127                 || ast.getParent().getType() == TokenTypes.LITERAL_CATCH
128                 || CheckUtils.isReceiverParameter(ast)
129                 || !matchAccessModifiers(getAccessModifier(ast))) {
130             checkName = false;
131         }
132         return checkName;
133     }
134 
135     /**
136      * Returns the access modifier of the method/constructor at the specified AST. If
137      * the method is in an interface or annotation block, the access modifier is assumed
138      * to be public.
139      *
140      * @param ast the token of the method/constructor.
141      * @return the access modifier of the method/constructor.
142      */
143     private static AccessModifier getAccessModifier(final DetailAST ast) {
144         final DetailAST params = ast.getParent();
145         final DetailAST meth = params.getParent();
146         AccessModifier accessModifier = AccessModifier.PRIVATE;
147 
148         if (meth.getType() == TokenTypes.METHOD_DEF
149                 || meth.getType() == TokenTypes.CTOR_DEF) {
150             if (ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
151                 accessModifier = AccessModifier.PUBLIC;
152             }
153             else {
154                 final DetailAST modsToken = meth.findFirstToken(TokenTypes.MODIFIERS);
155                 accessModifier = CheckUtils.getAccessModifierFromModifiersToken(modsToken);
156             }
157         }
158 
159         return accessModifier;
160     }
161 
162     /**
163      * Checks whether a method has the correct access modifier to be checked.
164      * @param accessModifier the access modifier of the method.
165      * @return whether the method matches the expected access modifier.
166      */
167     private boolean matchAccessModifiers(final AccessModifier accessModifier) {
168         return Arrays.stream(accessModifiers).anyMatch(el -> el == accessModifier);
169     }
170 
171     /**
172      * Checks whether a method is annotated with Override annotation.
173      * @param ast method parameter definition token.
174      * @return true if a method is annotated with Override annotation.
175      */
176     private static boolean isOverriddenMethod(DetailAST ast) {
177         boolean overridden = false;
178 
179         final DetailAST parent = ast.getParent().getParent();
180         final Optional<DetailAST> annotation =
181             Optional.ofNullable(parent.getFirstChild().getFirstChild());
182 
183         if (annotation.isPresent()
184                 && annotation.get().getType() == TokenTypes.ANNOTATION) {
185             final Optional<DetailAST> overrideToken =
186                 Optional.ofNullable(annotation.get().findFirstToken(TokenTypes.IDENT));
187             if (overrideToken.isPresent() && "Override".equals(overrideToken.get().getText())) {
188                 overridden = true;
189             }
190         }
191         return overridden;
192     }
193 }