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   * <p>
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   * </p>
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   * <p>
85   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
86   * </p>
87   * <p>
88   * Violation Message Keys:
89   * </p>
90   * <ul>
91   * <li>
92   * {@code ws.notFollowed}
93   * </li>
94   * <li>
95   * {@code ws.typeCast}
96   * </li>
97   * </ul>
98   *
99   * @since 3.0
100  */
101 @StatelessCheck
102 public class WhitespaceAfterCheck
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_WS_NOT_FOLLOWED = "ws.notFollowed";
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_TYPECAST = "ws.typeCast";
116 
117     @Override
118     public int[] getDefaultTokens() {
119         return getAcceptableTokens();
120     }
121 
122     @Override
123     public int[] getAcceptableTokens() {
124         return new int[] {
125             TokenTypes.COMMA,
126             TokenTypes.SEMI,
127             TokenTypes.TYPECAST,
128             TokenTypes.LITERAL_IF,
129             TokenTypes.LITERAL_ELSE,
130             TokenTypes.LITERAL_WHILE,
131             TokenTypes.LITERAL_DO,
132             TokenTypes.LITERAL_FOR,
133             TokenTypes.LITERAL_FINALLY,
134             TokenTypes.LITERAL_RETURN,
135             TokenTypes.LITERAL_YIELD,
136             TokenTypes.LITERAL_CATCH,
137             TokenTypes.DO_WHILE,
138             TokenTypes.ELLIPSIS,
139             TokenTypes.LITERAL_SWITCH,
140             TokenTypes.LITERAL_SYNCHRONIZED,
141             TokenTypes.LITERAL_TRY,
142             TokenTypes.LITERAL_CASE,
143             TokenTypes.LAMBDA,
144             TokenTypes.LITERAL_WHEN,
145         };
146     }
147 
148     @Override
149     public int[] getRequiredTokens() {
150         return CommonUtil.EMPTY_INT_ARRAY;
151     }
152 
153     @Override
154     public void visitToken(DetailAST ast) {
155         if (ast.getType() == TokenTypes.TYPECAST) {
156             final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN);
157             final int[] line = getLineCodePoints(targetAST.getLineNo() - 1);
158             if (!isFollowedByWhitespace(targetAST, line)) {
159                 log(targetAST, MSG_WS_TYPECAST);
160             }
161         }
162         else {
163             final int[] line = getLineCodePoints(ast.getLineNo() - 1);
164             if (!isFollowedByWhitespace(ast, line)) {
165                 final Object[] message = {ast.getText()};
166                 log(ast, MSG_WS_NOT_FOLLOWED, message);
167             }
168         }
169     }
170 
171     /**
172      * Checks whether token is followed by a whitespace.
173      *
174      * @param targetAST Ast token.
175      * @param line Unicode code points array of line associated with the ast token.
176      * @return true if ast token is followed by a whitespace.
177      */
178     private static boolean isFollowedByWhitespace(DetailAST targetAST, int... line) {
179         final int after =
180             targetAST.getColumnNo() + targetAST.getText().length();
181         boolean followedByWhitespace = true;
182 
183         if (after < line.length) {
184             final int codePoint = line[after];
185 
186             followedByWhitespace = codePoint == ';'
187                 || codePoint == ')'
188                 || Character.isWhitespace(codePoint);
189         }
190         return followedByWhitespace;
191     }
192 
193 }