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