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   * </li>
81   * </ul>
82   * <p>
83   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
84   * </p>
85   * <p>
86   * Violation Message Keys:
87   * </p>
88   * <ul>
89   * <li>
90   * {@code ws.notFollowed}
91   * </li>
92   * <li>
93   * {@code ws.typeCast}
94   * </li>
95   * </ul>
96   *
97   * @since 3.0
98   */
99  @StatelessCheck
100 public class WhitespaceAfterCheck
101     extends AbstractCheck {
102 
103     /**
104      * A key is pointing to the warning message text in "messages.properties"
105      * file.
106      */
107     public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed";
108 
109     /**
110      * A key is pointing to the warning message text in "messages.properties"
111      * file.
112      */
113     public static final String MSG_WS_TYPECAST = "ws.typeCast";
114 
115     @Override
116     public int[] getDefaultTokens() {
117         return getAcceptableTokens();
118     }
119 
120     @Override
121     public int[] getAcceptableTokens() {
122         return new int[] {
123             TokenTypes.COMMA,
124             TokenTypes.SEMI,
125             TokenTypes.TYPECAST,
126             TokenTypes.LITERAL_IF,
127             TokenTypes.LITERAL_ELSE,
128             TokenTypes.LITERAL_WHILE,
129             TokenTypes.LITERAL_DO,
130             TokenTypes.LITERAL_FOR,
131             TokenTypes.LITERAL_FINALLY,
132             TokenTypes.LITERAL_RETURN,
133             TokenTypes.LITERAL_YIELD,
134             TokenTypes.LITERAL_CATCH,
135             TokenTypes.DO_WHILE,
136             TokenTypes.ELLIPSIS,
137             TokenTypes.LITERAL_SWITCH,
138             TokenTypes.LITERAL_SYNCHRONIZED,
139             TokenTypes.LITERAL_TRY,
140             TokenTypes.LITERAL_CASE,
141             TokenTypes.LAMBDA,
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         if (ast.getType() == TokenTypes.TYPECAST) {
153             final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN);
154             final int[] line = getLineCodePoints(targetAST.getLineNo() - 1);
155             if (!isFollowedByWhitespace(targetAST, line)) {
156                 log(targetAST, MSG_WS_TYPECAST);
157             }
158         }
159         else {
160             final int[] line = getLineCodePoints(ast.getLineNo() - 1);
161             if (!isFollowedByWhitespace(ast, line)) {
162                 final Object[] message = {ast.getText()};
163                 log(ast, MSG_WS_NOT_FOLLOWED, message);
164             }
165         }
166     }
167 
168     /**
169      * Checks whether token is followed by a whitespace.
170      *
171      * @param targetAST Ast token.
172      * @param line Unicode code points array of line associated with the ast token.
173      * @return true if ast token is followed by a whitespace.
174      */
175     private static boolean isFollowedByWhitespace(DetailAST targetAST, int... line) {
176         final int after =
177             targetAST.getColumnNo() + targetAST.getText().length();
178         boolean followedByWhitespace = true;
179 
180         if (after < line.length) {
181             final int codePoint = line[after];
182 
183             followedByWhitespace = codePoint == ';'
184                 || codePoint == ')'
185                 || Character.isWhitespace(codePoint);
186         }
187         return followedByWhitespace;
188     }
189 
190 }