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 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.CommonUtil;
27  
28  /**
29   * <div>
30   * Checks that a token is followed by whitespace, with the exception that it
31   * does not check for whitespace after the semicolon of an empty for iterator.
32   * Use Check
33   * <a href="https://checkstyle.org/checks/whitespace/emptyforiteratorpad.html#EmptyForIteratorPad">
34   * EmptyForIteratorPad</a> to validate empty for iterators.
35   * </div>
36   * <ul>
37   * <li>
38   * Property {@code tokens} - tokens to check
39   * Type is {@code java.lang.String[]}.
40   * Validation type is {@code tokenSet}.
41   * Default value is:
42   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMMA">
43   * COMMA</a>,
44   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SEMI">
45   * SEMI</a>,
46   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPECAST">
47   * TYPECAST</a>,
48   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
49   * LITERAL_IF</a>,
50   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
51   * LITERAL_ELSE</a>,
52   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
53   * LITERAL_WHILE</a>,
54   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
55   * LITERAL_DO</a>,
56   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
57   * LITERAL_FOR</a>,
58   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
59   * LITERAL_FINALLY</a>,
60   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_RETURN">
61   * LITERAL_RETURN</a>,
62   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_YIELD">
63   * LITERAL_YIELD</a>,
64   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
65   * LITERAL_CATCH</a>,
66   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DO_WHILE">
67   * DO_WHILE</a>,
68   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ELLIPSIS">
69   * ELLIPSIS</a>,
70   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SWITCH">
71   * LITERAL_SWITCH</a>,
72   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_SYNCHRONIZED">
73   * LITERAL_SYNCHRONIZED</a>,
74   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
75   * LITERAL_TRY</a>,
76   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CASE">
77   * LITERAL_CASE</a>,
78   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
79   * LAMBDA</a>,
80   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHEN">
81   * LITERAL_WHEN</a>.
82   * </li>
83   * </ul>
84   *
85   * <p>
86   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
87   * </p>
88   *
89   * <p>
90   * Violation Message Keys:
91   * </p>
92   * <ul>
93   * <li>
94   * {@code ws.notFollowed}
95   * </li>
96   * <li>
97   * {@code ws.typeCast}
98   * </li>
99   * </ul>
100  *
101  * @since 3.0
102  */
103 @StatelessCheck
104 public class WhitespaceAfterCheck
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_WS_NOT_FOLLOWED = "ws.notFollowed";
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_TYPECAST = "ws.typeCast";
118 
119     @Override
120     public int[] getDefaultTokens() {
121         return getAcceptableTokens();
122     }
123 
124     @Override
125     public int[] getAcceptableTokens() {
126         return new int[] {
127             TokenTypes.COMMA,
128             TokenTypes.SEMI,
129             TokenTypes.TYPECAST,
130             TokenTypes.LITERAL_IF,
131             TokenTypes.LITERAL_ELSE,
132             TokenTypes.LITERAL_WHILE,
133             TokenTypes.LITERAL_DO,
134             TokenTypes.LITERAL_FOR,
135             TokenTypes.LITERAL_FINALLY,
136             TokenTypes.LITERAL_RETURN,
137             TokenTypes.LITERAL_YIELD,
138             TokenTypes.LITERAL_CATCH,
139             TokenTypes.DO_WHILE,
140             TokenTypes.ELLIPSIS,
141             TokenTypes.LITERAL_SWITCH,
142             TokenTypes.LITERAL_SYNCHRONIZED,
143             TokenTypes.LITERAL_TRY,
144             TokenTypes.LITERAL_CASE,
145             TokenTypes.LAMBDA,
146             TokenTypes.LITERAL_WHEN,
147         };
148     }
149 
150     @Override
151     public int[] getRequiredTokens() {
152         return CommonUtil.EMPTY_INT_ARRAY;
153     }
154 
155     @Override
156     public void visitToken(DetailAST ast) {
157         if (ast.getType() == TokenTypes.TYPECAST) {
158             final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN);
159             final int[] line = getLineCodePoints(targetAST.getLineNo() - 1);
160             if (!isFollowedByWhitespace(targetAST, line)) {
161                 log(targetAST, MSG_WS_TYPECAST);
162             }
163         }
164         else {
165             final int[] line = getLineCodePoints(ast.getLineNo() - 1);
166             if (!isFollowedByWhitespace(ast, line)) {
167                 final Object[] message = {ast.getText()};
168                 log(ast, MSG_WS_NOT_FOLLOWED, message);
169             }
170         }
171     }
172 
173     /**
174      * Checks whether token is followed by a whitespace.
175      *
176      * @param targetAST Ast token.
177      * @param line Unicode code points array of line associated with the ast token.
178      * @return true if ast token is followed by a whitespace.
179      */
180     private static boolean isFollowedByWhitespace(DetailAST targetAST, int... line) {
181         final int after =
182             targetAST.getColumnNo() + targetAST.getText().length();
183         boolean followedByWhitespace = true;
184 
185         if (after < line.length) {
186             final int codePoint = line[after];
187 
188             followedByWhitespace = codePoint == ';'
189                 || codePoint == ')'
190                 || Character.isWhitespace(codePoint);
191         }
192         return followedByWhitespace;
193     }
194 
195 }