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   * <div>
33   * Checks the padding between the identifier of a method definition,
34   * constructor definition, method call, constructor invocation, record, or record pattern;
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   * </div>
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#CTOR_CALL">
63   * CTOR_CALL</a>,
64   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_NEW">
65   * LITERAL_NEW</a>,
66   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
67   * METHOD_CALL</a>,
68   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
69   * METHOD_DEF</a>,
70   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SUPER_CTOR_CALL">
71   * SUPER_CTOR_CALL</a>,
72   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
73   * ENUM_CONSTANT_DEF</a>,
74   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
75   * RECORD_DEF</a>,
76   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_PATTERN_DEF">
77   * RECORD_PATTERN_DEF</a>.
78   * </li>
79   * </ul>
80   *
81   * <p>
82   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
83   * </p>
84   *
85   * <p>
86   * Violation Message Keys:
87   * </p>
88   * <ul>
89   * <li>
90   * {@code line.previous}
91   * </li>
92   * <li>
93   * {@code ws.notPreceded}
94   * </li>
95   * <li>
96   * {@code ws.preceded}
97   * </li>
98   * </ul>
99   *
100  * @since 3.4
101  */
102 
103 @StatelessCheck
104 public class MethodParamPadCheck
105     extends AbstractCheck {
106 
107     /**
108      * A key is pointing to the warning message text in "messages.properties"
109      * file.
110      */
111     public static final String MSG_LINE_PREVIOUS = "line.previous";
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_PRECEDED = "ws.preceded";
118 
119     /**
120      * A key is pointing to the warning message text in "messages.properties"
121      * file.
122      */
123     public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded";
124 
125     /**
126      * Allow a line break between the identifier and left parenthesis.
127      */
128     private boolean allowLineBreaks;
129 
130     /** Specify policy on how to pad method parameter. */
131     private PadOption option = PadOption.NOSPACE;
132 
133     @Override
134     public int[] getDefaultTokens() {
135         return getAcceptableTokens();
136     }
137 
138     @Override
139     public int[] getAcceptableTokens() {
140         return new int[] {
141             TokenTypes.CTOR_DEF,
142             TokenTypes.CTOR_CALL,
143             TokenTypes.LITERAL_NEW,
144             TokenTypes.METHOD_CALL,
145             TokenTypes.METHOD_DEF,
146             TokenTypes.SUPER_CTOR_CALL,
147             TokenTypes.ENUM_CONSTANT_DEF,
148             TokenTypes.RECORD_DEF,
149             TokenTypes.RECORD_PATTERN_DEF,
150         };
151     }
152 
153     @Override
154     public int[] getRequiredTokens() {
155         return CommonUtil.EMPTY_INT_ARRAY;
156     }
157 
158     @Override
159     public void visitToken(DetailAST ast) {
160         final DetailAST parenAST;
161         if (ast.getType() == TokenTypes.METHOD_CALL) {
162             parenAST = ast;
163         }
164         else {
165             parenAST = ast.findFirstToken(TokenTypes.LPAREN);
166             // array construction => parenAST == null
167         }
168 
169         if (parenAST != null) {
170             final int[] line = getLineCodePoints(parenAST.getLineNo() - 1);
171             if (CodePointUtil.hasWhitespaceBefore(parenAST.getColumnNo(), line)) {
172                 if (!allowLineBreaks) {
173                     log(parenAST, MSG_LINE_PREVIOUS, parenAST.getText());
174                 }
175             }
176             else {
177                 final int before = parenAST.getColumnNo() - 1;
178                 if (option == PadOption.NOSPACE
179                     && CommonUtil.isCodePointWhitespace(line, before)) {
180                     log(parenAST, MSG_WS_PRECEDED, parenAST.getText());
181                 }
182                 else if (option == PadOption.SPACE
183                          && !CommonUtil.isCodePointWhitespace(line, before)) {
184                     log(parenAST, MSG_WS_NOT_PRECEDED, parenAST.getText());
185                 }
186             }
187         }
188     }
189 
190     /**
191      * Setter to allow a line break between the identifier and left parenthesis.
192      *
193      * @param allowLineBreaks whether whitespace should be
194      *     flagged at line breaks.
195      * @since 3.4
196      */
197     public void setAllowLineBreaks(boolean allowLineBreaks) {
198         this.allowLineBreaks = allowLineBreaks;
199     }
200 
201     /**
202      * Setter to specify policy on how to pad method parameter.
203      *
204      * @param optionStr string to decode option from
205      * @throws IllegalArgumentException if unable to decode
206      * @since 3.4
207      */
208     public void setOption(String optionStr) {
209         option = PadOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
210     }
211 
212 }