1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
27  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
28  
29  /**
30   * <p>
31   * Checks the number of parameters that a method or constructor has.
32   * The default allowable number of parameters is 7.
33   * To change the number of allowable parameters, set property max.
34   * Allows to ignore number of parameters for methods with
35   * &#064;{@link Override} annotation.
36   * </p>
37   * <p>
38   * An example of how to configure the check is:
39   * </p>
40   * <pre>
41   * &lt;module name="ParameterNumber"/&gt;
42   * </pre>
43   * <p>
44   * An example of how to configure the check to allow 10 parameters
45   * and ignoring parameters for methods with &#064;{@link Override}
46   * annotation is:
47   * </p>
48   * <pre>
49   * &lt;module name="ParameterNumber"&gt;
50   *    &lt;property name="max" value="10"/&gt;
51   *    &lt;property name="ignoreOverriddenMethods" value="true"/&gt;
52   * &lt;/module&gt;
53   * </pre>
54   * Java code that will be ignored:
55   * <pre>
56   * {@code
57   *
58   *  &#064;Override
59   *  public void needsLotsOfParameters(int a,
60   *      int b, int c, int d, int e, int f, int g, int h) {
61   *      ...
62   *  }
63   * }
64   * </pre>
65   */
66  @StatelessCheck
67  public class ParameterNumberCheck
68      extends AbstractCheck {
69  
70      /**
71       * A key is pointing to the warning message text in "messages.properties"
72       * file.
73       */
74      public static final String MSG_KEY = "maxParam";
75  
76      /** {@link Override Override} annotation name. */
77      private static final String OVERRIDE = "Override";
78  
79      /** Canonical {@link Override Override} annotation name. */
80      private static final String CANONICAL_OVERRIDE = "java.lang." + OVERRIDE;
81  
82      /** Default maximum number of allowed parameters. */
83      private static final int DEFAULT_MAX_PARAMETERS = 7;
84  
85      /** The maximum number of allowed parameters. */
86      private int max = DEFAULT_MAX_PARAMETERS;
87  
88      /** Ignore overridden methods. */
89      private boolean ignoreOverriddenMethods;
90  
91      /**
92       * Sets the maximum number of allowed parameters.
93       * @param max the max allowed parameters
94       */
95      public void setMax(int max) {
96          this.max = max;
97      }
98  
99      /**
100      * Ignore number of parameters for methods with
101      * &#064;{@link Override} annotation.
102      * @param ignoreOverriddenMethods set ignore overridden methods
103      */
104     public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
105         this.ignoreOverriddenMethods = ignoreOverriddenMethods;
106     }
107 
108     @Override
109     public int[] getDefaultTokens() {
110         return getAcceptableTokens();
111     }
112 
113     @Override
114     public int[] getAcceptableTokens() {
115         return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF};
116     }
117 
118     @Override
119     public int[] getRequiredTokens() {
120         return CommonUtil.EMPTY_INT_ARRAY;
121     }
122 
123     @Override
124     public void visitToken(DetailAST ast) {
125         final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
126         final int count = params.getChildCount(TokenTypes.PARAMETER_DEF);
127         if (count > max && !shouldIgnoreNumberOfParameters(ast)) {
128             final DetailAST name = ast.findFirstToken(TokenTypes.IDENT);
129             log(name, MSG_KEY, max, count);
130         }
131     }
132 
133     /** Determine whether to ignore number of parameters for the method.
134      *
135      * @param ast the token to process
136      * @return true if this is overridden method and number of parameters should be ignored
137      *         false otherwise
138      */
139     private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
140         //if you override a method, you have no power over the number of parameters
141         return ignoreOverriddenMethods
142                 && (AnnotationUtil.containsAnnotation(ast, OVERRIDE)
143                 || AnnotationUtil.containsAnnotation(ast, CANONICAL_OVERRIDE));
144     }
145 
146 }