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.whitespace;
21  
22  import java.util.Locale;
23  
24  import com.puppycrawl.tools.checkstyle.StatelessCheck;
25  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26  import com.puppycrawl.tools.checkstyle.api.DetailAST;
27  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28  import com.puppycrawl.tools.checkstyle.utils.CodePointUtil;
29  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30  
31  /**
32   * <p>
33   * Checks the padding between the identifier of a method definition,
34   * constructor definition, method call, or constructor invocation;
35   * and the left parenthesis of the parameter list.
36   * That is, if the identifier and left parenthesis are on the same line,
37   * checks whether a space is required immediately after the identifier or
38   * such a space is forbidden.
39   * If they are not on the same line, reports a violation, unless configured to
40   * allow line breaks. To allow linebreaks after the identifier, set property
41   * {@code allowLineBreaks} to {@code true}.
42   * </p>
43   * <ul>
44   * <li>
45   * Property {@code allowLineBreaks} - Allow a line break between the identifier
46   * and left parenthesis.
47   * Type is {@code boolean}.
48   * Default value is {@code false}.
49   * </li>
50   * <li>
51   * Property {@code option} - Specify policy on how to pad method parameter.
52   * Type is {@code com.puppycrawl.tools.checkstyle.checks.whitespace.PadOption}.
53   * Default value is {@code nospace}.
54   * </li>
55   * <li>
56   * Property {@code tokens} - tokens to check
57   * Type is {@code java.lang.String[]}.
58   * Validation type is {@code tokenSet}.
59   * Default value is:
60   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
61   * CTOR_DEF</a>,
62   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">
63   * LITERAL_NEW</a>,
64   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
65   * METHOD_CALL</a>,
66   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
67   * METHOD_DEF</a>,
68   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">
69   * SUPER_CTOR_CALL</a>,
70   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
71   * ENUM_CONSTANT_DEF</a>,
72   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
73   * RECORD_DEF</a>.
74   * </li>
75   * </ul>
76   * <p>
77   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
78   * </p>
79   * <p>
80   * Violation Message Keys:
81   * </p>
82   * <ul>
83   * <li>
84   * {@code line.previous}
85   * </li>
86   * <li>
87   * {@code ws.notPreceded}
88   * </li>
89   * <li>
90   * {@code ws.preceded}
91   * </li>
92   * </ul>
93   *
94   * @since 3.4
95   */
96  
97  @StatelessCheck
98  public class MethodParamPadCheck
99      extends AbstractCheck {
100 
101     /**
102      * A key is pointing to the warning message text in "messages.properties"
103      * file.
104      */
105     public static final String MSG_LINE_PREVIOUS = "line.previous";
106 
107     /**
108      * A key is pointing to the warning message text in "messages.properties"
109      * file.
110      */
111     public static final String MSG_WS_PRECEDED = "ws.preceded";
112 
113     /**
114      * A key is pointing to the warning message text in "messages.properties"
115      * file.
116      */
117     public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
118 
119     /**
120      * Allow a line break between the identifier and left parenthesis.
121      */
122     private boolean allowLineBreaks;
123 
124     /** Specify policy on how to pad method parameter. */
125     private PadOption option = PadOption.NOSPACE;
126 
127     @Override
128     public int[] getDefaultTokens() {
129         return getAcceptableTokens();
130     }
131 
132     @Override
133     public int[] getAcceptableTokens() {
134         return new int[] {
135             TokenTypes.CTOR_DEF,
136             TokenTypes.LITERAL_NEW,
137             TokenTypes.METHOD_CALL,
138             TokenTypes.METHOD_DEF,
139             TokenTypes.SUPER_CTOR_CALL,
140             TokenTypes.ENUM_CONSTANT_DEF,
141             TokenTypes.RECORD_DEF,
142         };
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 parenAST;
153         if (ast.getType() == TokenTypes.METHOD_CALL) {
154             parenAST = ast;
155         }
156         else {
157             parenAST = ast.findFirstToken(TokenTypes.LPAREN);
158             // array construction => parenAST == null
159         }
160 
161         if (parenAST != null) {
162             final int[] line = getLineCodePoints(parenAST.getLineNo() - 1);
163             if (CodePointUtil.hasWhitespaceBefore(parenAST.getColumnNo(), line)) {
164                 if (!allowLineBreaks) {
165                     log(parenAST, MSG_LINE_PREVIOUS, parenAST.getText());
166                 }
167             }
168             else {
169                 final int before = parenAST.getColumnNo() - 1;
170                 if (option == PadOption.NOSPACE
171                     && CommonUtil.isCodePointWhitespace(line, before)) {
172                     log(parenAST, MSG_WS_PRECEDED, parenAST.getText());
173                 }
174                 else if (option == PadOption.SPACE
175                          && !CommonUtil.isCodePointWhitespace(line, before)) {
176                     log(parenAST, MSG_WS_NOT_PRECEDED, parenAST.getText());
177                 }
178             }
179         }
180     }
181 
182     /**
183      * Setter to allow a line break between the identifier and left parenthesis.
184      *
185      * @param allowLineBreaks whether whitespace should be
186      *     flagged at line breaks.
187      * @since 3.4
188      */
189     public void setAllowLineBreaks(boolean allowLineBreaks) {
190         this.allowLineBreaks = allowLineBreaks;
191     }
192 
193     /**
194      * Setter to specify policy on how to pad method parameter.
195      *
196      * @param optionStr string to decode option from
197      * @throws IllegalArgumentException if unable to decode
198      * @since 3.4
199      */
200     public void setOption(String optionStr) {
201         option = PadOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
202     }
203 
204 }