View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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.sizes;
21  
22  import java.util.Collections;
23  import java.util.Set;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
30  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
31  
32  /**
33   * <div>
34   * Checks the number of parameters of a method or constructor.
35   * </div>
36   * <ul>
37   * <li>
38   * Property {@code ignoreAnnotatedBy} - Ignore methods and constructors
39   * annotated with the specified annotation(s).
40   * Type is {@code java.lang.String[]}.
41   * Default value is {@code ""}.
42   * </li>
43   * <li>
44   * Property {@code ignoreOverriddenMethods} - Ignore number of parameters for
45   * methods with {@code @Override} annotation.
46   * Type is {@code boolean}.
47   * Default value is {@code false}.
48   * </li>
49   * <li>
50   * Property {@code max} - Specify the maximum number of parameters allowed.
51   * Type is {@code int}.
52   * Default value is {@code 7}.
53   * </li>
54   * <li>
55   * Property {@code tokens} - tokens to check
56   * Type is {@code java.lang.String[]}.
57   * Validation type is {@code tokenSet}.
58   * Default value is:
59   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
60   * METHOD_DEF</a>,
61   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
62   * CTOR_DEF</a>.
63   * </li>
64   * </ul>
65   *
66   * <p>
67   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
68   * </p>
69   *
70   * <p>
71   * Violation Message Keys:
72   * </p>
73   * <ul>
74   * <li>
75   * {@code maxParam}
76   * </li>
77   * </ul>
78   *
79   * @since 3.0
80   */
81  @StatelessCheck
82  public class ParameterNumberCheck
83      extends AbstractCheck {
84  
85      /**
86       * A key is pointing to the warning message text in "messages.properties"
87       * file.
88       */
89      public static final String MSG_KEY = "maxParam";
90  
91      /** Default maximum number of allowed parameters. */
92      private static final int DEFAULT_MAX_PARAMETERS = 7;
93  
94      /** Specify the maximum number of parameters allowed. */
95      private int max = DEFAULT_MAX_PARAMETERS;
96  
97      /** Ignore number of parameters for methods with {@code @Override} annotation. */
98      private boolean ignoreOverriddenMethods;
99  
100     /**
101      * Ignore methods and constructors annotated with the specified annotation(s).
102      */
103     private Set<String> ignoreAnnotatedBy = Collections.emptySet();
104 
105     /**
106      * Setter to specify the maximum number of parameters allowed.
107      *
108      * @param max the max allowed parameters
109      * @since 3.0
110      */
111     public void setMax(int max) {
112         this.max = max;
113     }
114 
115     /**
116      * Setter to ignore number of parameters for methods with {@code @Override} annotation.
117      *
118      * @param ignoreOverriddenMethods set ignore overridden methods
119      * @since 6.2
120      */
121     public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
122         this.ignoreOverriddenMethods = ignoreOverriddenMethods;
123     }
124 
125     /**
126      * Setter to ignore methods and constructors annotated with the specified annotation(s).
127      *
128      * @param annotationNames specified annotation(s)
129      * @since 10.15.0
130      */
131     public void setIgnoreAnnotatedBy(String... annotationNames) {
132         ignoreAnnotatedBy = Set.of(annotationNames);
133     }
134 
135     @Override
136     public int[] getDefaultTokens() {
137         return getAcceptableTokens();
138     }
139 
140     @Override
141     public int[] getAcceptableTokens() {
142         return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF};
143     }
144 
145     @Override
146     public int[] getRequiredTokens() {
147         return CommonUtil.EMPTY_INT_ARRAY;
148     }
149 
150     @Override
151     public void visitToken(DetailAST ast) {
152         final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
153         final int count = params.getChildCount(TokenTypes.PARAMETER_DEF);
154         if (count > max && !shouldIgnoreNumberOfParameters(ast)) {
155             final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
156             log(name, MSG_KEY, max, count);
157         }
158     }
159 
160     /**
161      * Determine whether to ignore number of parameters.
162      *
163      * @param ast the token to process
164      * @return true if number of parameters should be ignored.
165      */
166     private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
167         return isIgnoredOverriddenMethod(ast) || isAnnotatedByIgnoredAnnotations(ast);
168     }
169 
170     /**
171      * Checks if method is overridden and should be ignored.
172      *
173      * @param ast method definition to check
174      * @return true if method is overridden and should be ignored.
175      */
176     private boolean isIgnoredOverriddenMethod(DetailAST ast) {
177         return ignoreOverriddenMethods && AnnotationUtil.hasOverrideAnnotation(ast);
178     }
179 
180     /**
181      * Checks if method or constructor is annotated by ignored annotation(s).
182      *
183      * @param ast method or constructor definition to check
184      * @return true if annotated by ignored annotation(s).
185      */
186     private boolean isAnnotatedByIgnoredAnnotations(DetailAST ast) {
187         return AnnotationUtil.containsAnnotation(ast, ignoreAnnotatedBy);
188     }
189 
190 }