001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.whitespace; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 027 028/** 029 * <p> 030 * Checks that a token is followed by whitespace, with the exception that it 031 * does not check for whitespace after the semicolon of an empty for iterator. 032 * Use Check <a href="https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad"> 033 * EmptyForIteratorPad</a> to validate empty for iterators. 034 * </p> 035 * <ul> 036 * <li> 037 * Property {@code tokens} - tokens to check 038 * Type is {@code java.lang.String[]}. 039 * Validation type is {@code tokenSet}. 040 * Default value is: 041 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMMA"> 042 * COMMA</a>, 043 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SEMI"> 044 * SEMI</a>, 045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#TYPECAST"> 046 * TYPECAST</a>, 047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF"> 048 * LITERAL_IF</a>, 049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE"> 050 * LITERAL_ELSE</a>, 051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE"> 052 * LITERAL_WHILE</a>, 053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO"> 054 * LITERAL_DO</a>, 055 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR"> 056 * LITERAL_FOR</a>, 057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO"> 058 * DO_WHILE</a>. 059 * </li> 060 * </ul> 061 * <p> 062 * To configure the check: 063 * </p> 064 * <pre> 065 * <module name="WhitespaceAfter"/> 066 * </pre> 067 * <p>Example:</p> 068 * <pre> 069 * public void myTest() { 070 * if (foo) { // OK 071 * //... 072 * } else if(bar) { // violation 073 * //... 074 * } 075 * 076 * testMethod(foo, bar); // OK 077 * testMethod(foo,bar); // violation 078 * 079 * for (;;){} // OK 080 * for(;;){} // violation, space after 'for' is required 081 * } 082 * </pre> 083 * <p> 084 * To configure the check for whitespace only after COMMA and SEMI tokens: 085 * </p> 086 * <pre> 087 * <module name="WhitespaceAfter"> 088 * <property name="tokens" value="COMMA, SEMI"/> 089 * </module> 090 * </pre> 091 * <p>Example:</p> 092 * <pre> 093 * public void myTest() { 094 * int a; int b; // OK 095 * int a;int b; // violation 096 * 097 * testMethod(foo, bar); // OK 098 * testMethod(foo,bar); // violation 099 * 100 * for(;;) {} // OK 101 * } 102 * </pre> 103 * <p> 104 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 105 * </p> 106 * <p> 107 * Violation Message Keys: 108 * </p> 109 * <ul> 110 * <li> 111 * {@code ws.notFollowed} 112 * </li> 113 * <li> 114 * {@code ws.typeCast} 115 * </li> 116 * </ul> 117 * 118 * @since 3.0 119 */ 120@StatelessCheck 121public class WhitespaceAfterCheck 122 extends AbstractCheck { 123 124 /** 125 * A key is pointing to the warning message text in "messages.properties" 126 * file. 127 */ 128 public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed"; 129 130 /** 131 * A key is pointing to the warning message text in "messages.properties" 132 * file. 133 */ 134 public static final String MSG_WS_TYPECAST = "ws.typeCast"; 135 136 @Override 137 public int[] getDefaultTokens() { 138 return getAcceptableTokens(); 139 } 140 141 @Override 142 public int[] getAcceptableTokens() { 143 return new int[] { 144 TokenTypes.COMMA, 145 TokenTypes.SEMI, 146 TokenTypes.TYPECAST, 147 TokenTypes.LITERAL_IF, 148 TokenTypes.LITERAL_ELSE, 149 TokenTypes.LITERAL_WHILE, 150 TokenTypes.LITERAL_DO, 151 TokenTypes.LITERAL_FOR, 152 TokenTypes.DO_WHILE, 153 }; 154 } 155 156 @Override 157 public int[] getRequiredTokens() { 158 return CommonUtil.EMPTY_INT_ARRAY; 159 } 160 161 @Override 162 public void visitToken(DetailAST ast) { 163 if (ast.getType() == TokenTypes.TYPECAST) { 164 final DetailAST targetAST = ast.findFirstToken(TokenTypes.RPAREN); 165 final String line = getLine(targetAST.getLineNo() - 1); 166 if (!isFollowedByWhitespace(targetAST, line)) { 167 log(targetAST, MSG_WS_TYPECAST); 168 } 169 } 170 else { 171 final String line = getLine(ast.getLineNo() - 1); 172 if (!isFollowedByWhitespace(ast, line)) { 173 final Object[] message = {ast.getText()}; 174 log(ast, MSG_WS_NOT_FOLLOWED, message); 175 } 176 } 177 } 178 179 /** 180 * Checks whether token is followed by a whitespace. 181 * 182 * @param targetAST Ast token. 183 * @param line The line associated with the ast token. 184 * @return true if ast token is followed by a whitespace. 185 */ 186 private static boolean isFollowedByWhitespace(DetailAST targetAST, String line) { 187 final int after = 188 targetAST.getColumnNo() + targetAST.getText().length(); 189 boolean followedByWhitespace = true; 190 191 if (after < line.length()) { 192 final char charAfter = line.charAt(after); 193 followedByWhitespace = charAfter == ';' 194 || charAfter == ')' 195 || Character.isWhitespace(charAfter); 196 } 197 return followedByWhitespace; 198 } 199 200}